160. Intersection of Two Linked Lists

做這道題目的時候順帶複習了一下找帶環單鏈表的環入口,在第二種解法中將作解釋。

描述:
Write a program to find the node at which the intersection of two singly linked lists begins.

For example, the following two linked lists:

A:          a1 → a2
                   ↘
                     c1 → c2 → c3
                   ↗            
B:     b1 → b2 → b3

意:找到其中的相交節點(修改一下也可以叫做環的入口節點),很明顯,圖中的入口是c1.

解法1

分析:
我們可以將這兩條鏈表看成是兩條平行的線段,其中後面的一段線段是一定相同的,只不過前面一段有長有短:

    a1  a2  c1  c2  c3 
b1  b2  b3  c1  c2  c3
//很容易看出來其實就是c1前面的一部分元素個數不同

那麼我們可以這樣來使得它們同步起來:
1.先讓a,b兩條鏈走完統計出兩條鏈表的長度爲a1,a2
2.找出其中長的一條鏈,讓它先往前走abs(a1-a2)個節點,這時候將長鏈表中多餘的元素走完了
3.另外一條鏈也同時開始走,這時候兩條鏈表剩餘的長度是一樣的,走到第一個相等的節點就是入口節點了。

C++

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* A = headA;
        ListNode* B = headB;
        int a = 0;
        int b = 0;
        while(A){
            a++;
            A = A->next;
        }
        while(B){
            b++;
            B = B->next;
        }
        //這裏需要加一個判斷兩條鏈表沒有交點的情況,注意
        if(A != B){return NULL;}
        int Max = max(a,b) - min(a,b); //或者abs(a-b),一樣
        if(a>b){
            while(Max){
                headA = headA->next;
                Max--;
            }
        }
        else{
            while(Max){
                headB = headB->next;
                Max--;
            }
        }
        while(headA != headB){
            headA = headA->next;
            headB = headB->next;
        }
        return headA;
} 
};

python:

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        A = headA
        B = headB
        a = 0
        b = 0
        while A is not None:
            a += 1
            A = A.next
        while B is not None:
            b += 1
            B = B.next
        if A != B:
            return None
        Max = abs(a-b)
        if a > b:
            while Max:
                headA = headA.next
                Max -= 1
        else:
            while Max:
                headB = headB.next
                Max -= 1
        while headA != headB:
                headA = headA.next
                headB = headB.next
        return headA

解法二:

分析:
這道題目其實和我們之前做過的一道判斷單鏈表是否有環很相像,我們只需要稍微構造一下即可還原那道題目的樣子。
比如還是上面的例子

A:          a1 → a2
                   ↘
                     c1 → c2 → c3
                   ↗            
B:     b1 → b2 → b3
//我們這做,將c3 -> next = b1

那麼就變成如下的圖了:
這裏寫圖片描述

這樣就構成一個環了,關於環我們可以瞭解到:使用兩個指針,一快一慢,我們能根據兩個指針是否相交來判斷這個鏈表是不是有環,但是要找到環的入口呢?這裏我們需要通過一些推導:
參考了這篇博客
大致過程咱們也來推導一下,加深自己的理解:
這裏寫圖片描述

這裏是黑色的部分是慢指針走過的路徑,紅色部分是快指針繞環經過的路徑:
假設

L1:平行線的長度,o點到m點
L2:整個環的長度
L_m_n:入口點m和相交點n的距離,按着箭頭指示的方向走
slow_len : 慢指針走過的距離
k : 快指針走過的圈數

我們能得到幾個公式:
1.L1 + L_m_n = slow_len
2.slow_len = L2 - L_m_n
3.L1 + k*L2 + L_m_n = slow_len

綜合這3個等式可以得到:
L1 = k*L2 - L_m_n = (k-1)L2 + (L2 - L_m_n)

這個式子拿到圖中就是:L1的距離 = 環的整數倍數(k-1倍) + 交點與環入口的距離,更具體一點:就是紅色線段和平行線段等長

也就是說,找到交點之後,再往前走L1步就能到達環的入口,這時候讓鏈表頭和這個交點同時向前走,如果是有環,則必然能相交,否則爲nullptr。

C++

//時間複雜度是O(n),空間複雜度是O(1)
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* head = headA;
        if (headA == NULL || headB == NULL) return NULL;
        while(headA->next){
            headA = headA->next;
        }
        headA->next = headB;
        ListNode* res = iscycle(head);
        headA->next = nullptr;

        return res;
    }
    ListNode* iscycle(ListNode* head){
        ListNode* slow = head;
        ListNode* fast = head;
        while(slow && fast){
            slow = slow->next;
            fast = fast->next;
            if(fast){
                fast = fast->next;
            }
            if(fast == slow){
                break;
            }
        }
        if (fast == nullptr){return nullptr;}  
        slow = head;
        while (slow != fast) {  
            slow = slow->next;  
            fast = fast->next;  
        }  
        return fast;     
    }
};

python:

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        if not headA or not headB:
            return None
        ptr = headA
        while ptr.next != None:
            ptr = ptr.next
        ptr.next = headB
        res = self.iscycle(headA)
        ptr.next = None
        return res

    def iscycle(self,head):
        slow = head
        fast = head
        while slow and fast:
            slow = slow.next
            fast = fast.next
            if fast:
                fast = fast.next
            if fast == slow:
                break
        if fast == None:
            return None
        slow = head
        while slow != fast:
            slow = slow.next
            fast = fast.next
        return fast
發佈了67 篇原創文章 · 獲贊 99 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章