[leetCode][003] Intersection of Two Linked Lists

菜鳥加貝的爬升發表於2015-02-04

【題目】:  

  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
   begin to intersect at node c1.

  Notes:
    1. If the two linked lists have no intersection at all, return null.
    2. The linked lists must retain their original structure after the function returns.
    3. You may assume there are no cycles anywhere in the entire linked structure.
    4. Your code should preferably run in O(n) time and use only O(1) memory

【題意解析】:

  輸入兩個單連結串列,判斷這兩個單連結串列是否相交,返回相交點。這裡有幾種方法供大家參考。

 

【解法1】

  《程式設計之美》一書中有講到該問題,如何判斷這兩個連結串列相交,可以講其中一個連結串列的頭尾相連,然後從另一個連結串列的角度來判斷時候有環狀連結串列存在即可。相當於將問題轉換成如何求有環單連結串列的環狀入口點?詳細方案見我的另一篇專門講環狀單連結串列的文章 《關於有環單連結串列的那些事兒》。需要注意的是,不能改變原有連結串列的結構,因此方法返回的時候需要恢復原狀。

  (1)將連結串列B的尾節點指向其頭節點HeadB,因此連結串列B形成環狀,連結串列A成為有環單連結串列;

  (2)此時問題轉換成求連結串列A上環狀入口點;

  (3)恢復連結串列B的結構;

  程式碼如下:

 1 /**
 2  * Definition for singly-linked list.
 3  * struct ListNode {
 4  *     int val;
 5  *     ListNode *next;
 6  *     ListNode(int x) : val(x), next(NULL) {}
 7  * };
 8  */
 9 class Solution {
10 public:
11     ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
12         if (nullptr == headA || nullptr == headB){
13             return nullptr;
14         }
15 
16         ListNode *pNode = headB;
17         while (pNode->next){
18             pNode = pNode->next;
19         }
20         ListNode *pRear = pNode;
21         pNode->next = headB;
22 
23         ListNode *pJoint = loopJoint(headA);
24         pRear->next = nullptr;   // 恢復
25 
26         return pJoint;
27     }
28     
29 private:    
30     ListNode *loopJoint(ListNode *pHead){
31         if (nullptr == pHead || nullptr == pHead->next){
32             return nullptr;
33         }
34 
35         ListNode *pSlow = pHead;
36         ListNode *pFast = pHead;
37         while (pFast && pFast->next){
38             pFast = pFast->next->next;
39             pSlow = pSlow->next;
40 
41             if (pFast == pSlow){
42                 break;
43             }
44         }
45 
46         if (nullptr == pFast || nullptr == pFast->next){
47             return nullptr;
48         }
49 
50         pSlow = pHead;
51         while (pFast != pSlow){
52             pFast = pFast->next;
53             pSlow = pSlow->next;
54         }
55 
56         return pSlow;
57     }
58 };

 

【解法2】 

  《劍指Offer》一書中提到的這個方法,如果兩個沒有環的連結串列相交於某個節點,那麼在這個節點之後的所有節點都是兩個連結串列所共有的。因此兩個連結串列從交織點之前的部分長度差即為整條連結串列的長度差,我們只要讓兩條連結串列從離交織點相同距離的位置開始前進,經過O(n)步,一定能夠找到交織點。

  (1)遍歷連結串列A,記錄其長度len1,遍歷連結串列B,記錄其長度len2。

  (2)按尾部對齊,如果兩個連結串列的長度不相同,讓長度更長的那個連結串列從頭節點先遍歷abs(len1-en2),這樣兩個連結串列指標指向對齊的位置。

  (3)然後兩個連結串列齊頭並進,當它們相等時,就是交集的節點。

  時間複雜度O(n+m),空間複雜度O(1)

  程式碼如下:

 1 /**
 2  * Definition for singly-linked list.
 3  * struct ListNode {
 4  *     int val;
 5  *     ListNode *next;
 6  *     ListNode(int x) : val(x), next(NULL) {}
 7  * };
 8  */
 9 class Solution {
10 public:
11     ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
12         if (nullptr == headA || nullptr == headB){
13             return nullptr;
14         }
15 
16         int nLongLength = 0;
17         int nShortLength = 0;
18         ListNode *pLongList = headA;
19         ListNode *pShortList = headB;
20 
21         while (pLongList){
22             ++nLongLength;
23             pLongList = pLongList->next;
24         }
25 
26         while (pShortList){
27             ++nShortLength;
28             pShortList = pShortList->next;
29         }
30 
31         if (nShortLength > nLongLength){
32             pLongList = headB;
33             pShortList = headA;
34 
35             // 校正
36             nLongLength ^= nShortLength;
37             nShortLength ^= nLongLength;
38             nLongLength ^= nShortLength;
39         }
40         else{
41             pLongList = headA;
42             pShortList = headB;
43         }
44 
45        // int offset = (nLongLength - nShortLength > 0) ? (nLongLength - nShortLength) : (nShortLength - nLongLength);
46         int offset = nLongLength - nShortLength;
47         while (offset> 0){
48             pLongList = pLongList->next;
49             --offset;
50         }
51 
52         while (pLongList != pShortList){
53             pLongList = pLongList->next;
54             pShortList = pShortList->next;
55         }
56 
57         return pLongList;
58     }
59 };

【解法3】

  還有一種解法,實際上還是利用步差來實現的,具體證明還米有搞懂,思考中。具體如下:

    (1)維護兩個指標pA和pB,初始分別指向連結串列A和連結串列B。然後讓它們分別遍歷整個連結串列,每步一個節點。

    (2)當pA到達連結串列末尾時,讓它指向B的頭節點;類似的當pB到達連結串列末尾時,重新指向A的頭節點。

    (3)當pA和pB相遇時也即為交織點。

  該演算法的時間複雜度依然為O(m + n),時間複雜度為O(1)

  程式碼如下:

 1 /**
 2  * Definition for singly-linked list.
 3  * struct ListNode {
 4  *     int val;
 5  *     ListNode *next;
 6  *     ListNode(int x) : val(x), next(NULL) {}
 7  * };
 8  */
 9 class Solution {
10 public:
11     ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
12         if (nullptr == headA || nullptr == headB){
13             return nullptr;
14         }
15 
16         ListNode *nodeA = headA;
17         ListNode *nodeB = headB;
18         while (nodeA && nodeB && nodeA != nodeB){
19             nodeA = nodeA->next;
20             nodeB = nodeB->next;
21 
22             if (nodeA == nodeB){
23                 break;
24             }
25 
26             if (nullptr == nodeA){
27                 nodeA = headB;
28             }
29 
30             if (nullptr == nodeB){
31                 nodeB = headA;
32             }
33         }
34 
35         return nodeA;
36     }
37 };

  如果錯誤,希望各位不吝賜教,謝謝

 

相關文章