LeetCode 相交鏈表

編寫一個程序,找到兩個單鏈表相交的起始節點。python

 

例如,下面的兩個鏈表:spa

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

在節點 c1 開始相交。.net

 

注意:指針

  • 若是兩個鏈表沒有交點,返回 null.
  • 在返回結果後,兩個鏈表仍須保持原有的結構。
  • 可假定整個鏈表結構中沒有循環。
  • 程序儘可能知足 O(n) 時間複雜度,且僅用 O(1) 內存。

 

 方法一:code

 1 /* C++ */
 2 /**  3  * Definition for singly-linked list.  4  * struct ListNode {  5  * int val;  6  * ListNode *next;  7  * ListNode(int x) : val(x), next(NULL) {}  8  * };  9  */
10 class Solution { 11 public: 12     ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { 13         if(headA == NULL || headB == NULL){ 14             return NULL; 15  } 16         ListNode *a = headA; 17         ListNode *b = headB; 18         // 計算A和B的長度
19         int lenA = 0,lenB = 0; 20         while(a->next != NULL){ 21             a = a->next; 22             lenA++; 23  } 24         while(b->next != NULL){ 25             b = b->next; 26             lenB++; 27  } 28         int step = lenB - lenA; 29         if(step>0){ 30             //說明B長
31             a = headB; 32             b = headA; 33         }else{// 說明A長
34             a = headA; 35             b = headB; 36             step *= -1; 37  } 38         while(step -- ){ 39             a = a->next; 40  } 41         while(a != b){ 42             a = a->next;b = b->next; 43  } 44         return a; 45  } 46 };

 python:blog

 1 # Definition for singly-linked list.
 2 # class ListNode(object):
 3 # def __init__(self, x):
 4 # self.val = x
 5 # self.next = None
 6 
 7 class Solution(object):  8     def getIntersectionNode(self, headA, headB):  9         """
10  :type head1, head1: ListNode 11  :rtype: ListNode 12         """
13         if headA==None or headB==None: 14             return None 15          # 計算A和B的長度
16         lenA, lenB = 0, 0 17         a, b = headA, headB 18         while a.next != None: 19             a = a.next 20             lenA += 1
21         while b.next != None: 22             b = b.next 23             lenB += 1
24         if lenA > lenB: 25             step, a, b = lenA - lenB, headA, headB 26         else: 27             step, a, b = lenB - lenA, headB, headA 28         
29         while step: 30             step -= 1
31             a = a.next 32         
33         while a != b: 34             a, b = a.next, b.next 35         return a

 

 

方法二:圖片

  原理來自:如何判斷單鏈表是否有環、環的入口、環的長度和總長 - CSDN博客內存

1)首先判斷鏈表是否有環get

  要想判斷有環,咱們能夠聯繫實際生活中的例子,很容易就想到操場上跑圈,由於是環形,因此快的確定會追上慢的,因此咱們能夠應用到鏈表上,用一個快指針和一個慢指針,可是細想一下發現,咱們在跑操的時候相遇時座標位置不必定是整數啊(這裏相比鏈表節點而言的),而鏈表是一個節點鏈接起來,咱們怎麼作,能讓他們在節點上相遇呢,這裏就要爲2個指針找到合適的速度,使之可以恰巧在某一結點上相遇。博客

原理:若是快的指針走到NULL,說明無環;而fast==slow相遇,則證實確定存在環。

公式推導

  爲何存在環的狀況下,兩個指針會相遇呢?如下推到n都是指 環長!

這裏寫圖片描述 
1.假定2個指針同一個起點 
  們讓兩個指針所有指向頭節點,而後給slow指針的速度爲一步,而fast指針的速度爲M步,則在第i次迭代的時候,slow指針走到i mod n,而fast指向Mi mod n,要想slow和fast相遇,則i mod n=Mi mod n,(M-1)i mod n,則咱們能夠令M=2(最小的能夠取得值),i mod n = 0,則 i=n時,相遇,因此咱們能夠給fast 2倍的速度,這樣它們會在 最後一個節點相遇。 
2.假定不在同一個起點,而且fast提早K位置 
  其實這個相似鏈表中含有個小環的狀況,即不是全部點在環中的狀況,這樣當slow即將進入環狀的時候,fast已經在環中k mod n位置了,因此問題轉化爲假定不在同一個起點,而且fast提早K位置,是否會在一點相遇? 
這裏寫圖片描述
  fast的速度仍設置爲2倍,假定第i次迭代時,slow指向i mod n,fast指向k+2i mod n,其k大於0小於你,那麼i ≡ (2i+k)(mod n) -> (i+k) mod n = 0 -> 當i=n-k時,p與q相遇。 
這裏寫圖片描述
變相理解,如何同一個起點出發,他們會在整圈(也就是最後一個節點)相遇,如今fast在提早K位置出發,這樣就會使相遇點原本是最後節點,如今fast少走k步,便可與slow相遇,因此在n-K位置相遇。相似問題求倒數第K個節點:http://blog.csdn.net/dawn_after_dark/article/details/73611115 
因此無論是圖1的鏈表,仍是圖2的鏈表,只要有環,快指針跟慢指針相遇,逆命題也成立;全部當快指針跟慢指針相遇,就必定存在環。 

2)找到兩鏈表的交點

  咱們已經在上面的討論中,已經得知slow與fast會在環中n-k位置相遇,咱們先靠主觀方面來探討這個問題,兩個指針同時從頭節點開始走,當慢指針即將進入環中的時候,快指針位於k mod n,說明慢指針走的這段路程也能對應k mod n, 由於快指針是慢指針速度的2倍,因此快指針在環中走的距離與慢指針走的距離同樣。而咱們發現相遇點位於n-k,再走k步就能夠到達環的入口,而且慢指針走的路程也能對應k mod n,因此咱們再令取2指針,一個指向頭節點,另外一個指向碰撞點,都以1步的速度前進,這兩個指針相遇點就是環的入口,這個結論適用於全環的鏈表,由於這時k=0,頭節點走一步就到了環的入口了。 
  以上只是咱們主觀的理解方式,若是採用推導呢,slow走過的路程爲s,環長爲n,因此,2s=s+k+(m-1)n,化簡爲s=k+(m-1)n,因此slow在環外至關於走了k+(m-1)n。 
  而碰撞點位於n-k的位置,因此要想走到環入點,則須要走k+mn步,這時你就會發現只要讓這兩個指針從頭節點與碰撞點以一步的速度過來,就必定會在環入點相遇,從而求出環入點!

 

簡言之:

  若存在環:

  slow指針速度爲1,fast指針速度爲2。當slow到達環入口點時,fast已經在環內走了k步,這時候slow總共走了k步,fast總共走了2*k步,也就是環外的鏈表的長度爲k。若slow又走了i步,與fast相遇(此時fast又走了2*i步),這時候有

 i ≡ (2i+k)(mod n) 

即:(i+k) mod n = 0

即:當i=n-k時,p與q相遇

  因此這時候再從相遇點走k步即到達環的入口點。因此咱們可使用一個新指針new從環外鏈表的頭開始走,同時slow繼續走,直到相遇,相遇點即爲環的入口點。

 1 # Definition for singly-linked list.
 2 # class ListNode(object):
 3 # def __init__(self, x):
 4 # self.val = x
 5 # self.next = None
 6 
 7 class Solution(object):  8     def getIntersectionNode(self, headA, headB):  9         """
10  :type head1, head1: ListNode 11  :rtype: ListNode 12         """
13         if headA == None or headB == None: 14             return None 15         
16         # 檢查是否有交點, 若是沒有交點,返回None
17         a, b = headA, headB 18         while a.next!=None: 19             a = a.next 20         while b.next!=None: 21             b = b.next 22         if a.val != b.val: 23             return None 24         
25         # 接下來找出交點,先將鏈表B的首尾相連
26         b = headB 27         while b.next != None: 28             b = b.next 29         b.next = headB 30 
31         a = headA.next #慢指針
32         c = headA.next.next #快指針
33         while a.val != c.val: # 找相遇點
34             a, c = a.next, c.next.next 35 
36         c = headA 37         while a.val != c.val: 38             a, c = a.next, c.next 39             
40         # 找到相交點以後,須要將兩鏈表復原
41         b.next = None 42 
43         return c

咱們看到這種方法貌似沒有方法一高效::

相關文章
相關標籤/搜索