《剑指offer》两个链表的第一个公共节点

注:此博客不再更新,所有最新文章将发表在个人独立博客limengting.site。分享技术,记录生活,欢迎大家关注

题目描述
输入两个链表,找出它们的第一个公共结点。

根据提交结果来看,只需要考虑无环单链表:

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
  if (pHead1 == null || pHead2 == null) return null;
        // 空间复杂度O(1)的方法:
        // 先遍历得到链表一和链表二的长度和最后一个节点,如果end1 != end2则不可能相交
        // end1 == end2,若链表1长度为100,链表2长度为80,则head1先走20,然后head1和head2一起走,相遇处即第一个相交节点处
        ListNode cur = pHead1;
        int count = 0;
        while (cur.next != null) {
            cur = cur.next;
            count++;
        }
        ListNode end1 = cur;
        cur = pHead2;
        while (cur.next != null) {
            cur = cur.next;
            count--;
        }
        ListNode end2 = cur;
        if (end1 != end2) return null;
        ListNode longer = count > 0 ? pHead1 : pHead2;
        ListNode shorter = longer == pHead1 ? pHead2 : pHead1;
        count = Math.abs(count);
        while (count > 0) {
            longer = longer.next;
            count--;
        }
        while (longer != shorter) {
            longer = longer.next;
            shorter = shorter.next;
        }
        return longer;
    }
}

若考虑链表有环和无环的情况:

/*
public class ListNode {
    int val;
    ListNode next = null;
 
    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
   // 2、O(1),准备两个指针,快指针一次走两步,慢指针一次走一步
    // 如果快指针遇到空,返回无环
    // 如果快指针和慢指针相遇,快指针回到开头,以后快指针和慢指针都一次走一步,快指针和慢指针会在入环节点处相遇
    // 2、不使用辅助空间
    // 快指针一次走两步,慢指针一次走一步,如果快指针遇到空则返回无环
    // 如果有环则快指针和慢指针一定会相遇,相遇后快指针回到开头,以后快指针和慢指针都一次走一步,相遇处即为入环节点
    public static ListNode getLoopNode2(ListNode head) {
        if (head == null) return null;
        ListNode fast = head.next.next;
        ListNode slow = head.next;
        while (fast != slow) {
            if (fast == null || fast.next == null) {
                return null;
            }
            slow = slow.next;
            fast = fast.next.next;
        }
        fast = head;
        while (fast != slow) {
            slow = slow.next;
            fast = fast.next;
        }
        return fast;
    }
 
    // 找到第一个相交的节点
    // 1、判断两个无环单链表是否相交:
    // (1) O(n):链表一的节点都放到map里,遍历链表二,第一个在map中的节点为相交节点,如果遍历到空都没找到在map中的则不相交
    // (2) O(1):先遍历链表一和链表二,统计链表一、二的长度和链表一、二的最后一个节点
    //            如果end1 != end2,链表一和链表二不可能相交
    //            如果end1 == end2,长度一100和长度二80比较,head1先走20步,然后head1和head2一起走,他们一定会共同走到第一个相交的节点处
    // 2、一个有环一个无环的单链表,不可能相交
    // 3、两个有环的单链表:三种情况
    public static ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if (pHead1 == null || pHead2 == null) return null;
        // loop1为链表1的入环节点
        // loop2为链表2的入环节点
        ListNode loop1 = getLoopNode2(pHead1);
        ListNode loop2 = getLoopNode2(pHead2);
 
        // 两个链表都无环
        if (loop1 == null && loop2 == null) {
            return noLoop2(pHead1, pHead2);
        }
        if (loop1 != null && loop2 != null) {
            return bothLoop(pHead1, pHead2, loop1, loop2);
        }
        return null;
    }
 
    private static ListNode noLoop2(ListNode pHead1, ListNode pHead2) {
        // 空间复杂度O(1)的方法:
        // 先遍历得到链表一和链表二的长度和最后一个节点,如果end1 != end2则不可能相交
        // end1 == end2,若链表1长度为100,链表2长度为80,则head1先走20,然后head1和head2一起走,相遇处即第一个相交节点处
        ListNode cur = pHead1;
        int count = 0;
        while (cur.next != null) {
            cur = cur.next;
            count++;
        }
        ListNode end1 = cur;
        cur = pHead2;
        while (cur.next != null) {
            cur = cur.next;
            count--;
        }
        ListNode end2 = cur;
        if (end1 != end2) return null;
        ListNode longer = count > 0 ? pHead1 : pHead2;
        ListNode shorter = longer == pHead1 ? pHead2 : pHead1;
        count = Math.abs(count);
        while (count > 0) {
            longer = longer.next;
            count--;
        }
        while (longer != shorter) {
            longer = longer.next;
            shorter = shorter.next;
        }
        return longer;
    }
 
    private static ListNode bothLoop(ListNode pHead1, ListNode pHead2, ListNode loop1, ListNode loop2) {
        if (loop1 == loop2) {
            return bothLoopCase2(pHead1, pHead2, loop1);
        }
 
        ListNode cur1 = loop1;
        while (cur1 != null) {
            if (cur1 == loop2) {
                return loop1; // case3
            }
            cur1 = cur1.next;
        }
        return null; // case1
    }
 
    // 此情况下等同于无环链表相交
    private static ListNode bothLoopCase2(ListNode pHead1, ListNode pHead2, ListNode loop) {
        ListNode cur = pHead1;
        int count = 0;
        while (cur.next != loop) {
            cur = cur.next;
            count++;
        }
        ListNode end1 = cur;
        cur = pHead2;
        while (cur.next != loop) {
            cur = cur.next;
            count--;
        }
        ListNode end2 = cur;
        if (end1 != end2) return null;
        ListNode longer = count > 0 ? pHead1 : pHead2;
        ListNode shorter = longer == pHead1 ? pHead2 : pHead1;
        count = Math.abs(count);
        while (count > 0) {
            longer = longer.next;
            count--;
        }
        while (longer != shorter) {
            longer = longer.next;
            shorter = shorter.next;
        }
        return longer;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章