《劍指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;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章