做這道題目的時候順帶複習了一下找帶環單鏈表的環入口,在第二種解法中將作解釋。
描述:
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