我們學一個算法,一定是為了用吧,所謂“學以致用”嗎?那么判斷兩個鏈表是否相交有什么用呢?這是因為一旦兩個鏈表出現相交的情況,就可能發生這樣的情況,程序釋放了鏈表La的所有節點,這樣就導致了另外一個與之有相交節點的鏈表Lb中的節點也釋放了,而Lb的使用者,可能並不知道事實的真相,這會帶來很大的麻煩。
1.問題分析
看看兩個鏈表相交到底是怎么回事吧,有這樣的的幾個事實:(假設鏈表中不存在環)
(1)一旦兩個鏈表相交,那么兩個鏈表中的節點一定有相同地址。
(2)一旦兩個鏈表相交,那么兩個鏈表從相交節點開始到尾節點一定都是相同的節點。
分析出來了問題的本質,那么思路也就自然有了。
2.問題解法
2.1 哈希解法:
既然連個鏈表一旦相交,相交節點一定有相同的內存地址,而不同的節點內存地址一定是不同的,那么不妨利用內存地址建立哈希表,如此通過判斷兩個鏈表中是否存在內存地址相同的節點判斷兩個鏈表是否相交。具體做法是:遍歷第一個鏈表,並利用地址建立哈希表,遍歷第二個鏈表,看看地址哈希值是否和第一個表中的節點地址值有相同即可判斷兩個鏈表是否相交。
時間復雜度O(length1 + length2)
空間復雜度O(length1)
分析:時間復雜度是線性的,可以接受,並且可以順便找到第一個相交節點,但是卻增加了O(length1)的空間復雜度,這顯然不能令人滿意。
2.2 問題轉化
如果兩個鏈表中存在相交節點,那么將第二個鏈表接到第一個鏈表的后面,然后從第二個鏈表的表頭開始遍歷,如果存在環,則遍歷過程一定會回到鏈表二的表頭節點。可是這種方法似乎並不能找到第一個相交節點。怎么辦呢?怎樣才能判斷鏈表中是否存在環,並且找到環的開始節點呢?
網上看到了這樣的一個解法:設置兩個指針fast和slow,初始值都指向頭,slow每次前進一步,fast每次前進二步,如果鏈表存在環,則fast必定先進入環,而slow后進入環,兩個指針必定相遇。(當然,fast先行頭到尾部為NULL,則為無環鏈表),這樣就可以判斷兩個鏈表是否相交了,程序如下:
{
slist * slow = head , * fast = head;
while ( fast && fast -> next )
{
slow = slow -> next;
fast = fast -> next -> next;
if ( slow == fast )
}
return ! (fast == NULL || fast -> next == NULL);
}
下面看看怎么找環的入口,當fast與slow相遇時,slow肯定沒有走遍歷完鏈表,而fast已經在環內循環了n圈(1<=n)。假設slow走了s步,則fast走了2s步(fast步數還等於s 加上在環上多轉的n圈),設環長為r,則:
2s = s + nr
s= nr
設整個鏈表長L,入口環與相遇點距離為x,起點到環入口點的距離為a。
a + x = nr
a + x = (n – 1)r +r = (n-1)r + L - a
a = (n-1)r + (L – a – x)
(L – a – x)為相遇點到環入口點的距離,由此可知,從鏈表頭到環入口點等於(n-1)循環內環+相遇點到環入口點(從相遇點向后遍歷循環回到入口點的距離),於是我們從鏈表頭、與相遇點分別設一個指針,每次各走一步,兩個指針必定相遇,且相遇點為環入口點,也即為兩個鏈表的第一個相同節點。程序描述如下:
slist* FindLoopPort(slist *head) { slist *slow = head, *fast = head; while ( fast && fast->next ) { slow = slow->next; fast = fast->next->next; if ( slow == fast ) break; } if (fast == NULL || fast->next == NULL) return NULL; slow = head; while (slow != fast) { slow = slow->next; fast = fast->next; } return slow; }
這種解法似乎是非常犀利,邏輯推理很棒,但是這種解法的時間復雜度是怎么樣的呢??slow每次前進一步,fast每次前進兩步,這樣的話遍歷多少步會結束呢???(求人解釋)
2.3 抓住要點
不妨遍歷每個鏈表保存最后一個節點,看看最后一個節點是否是同一個節點,這種情況時間復雜度是O(length1 + length2)。基本也不需要什么空間,似乎是一個不錯的想法哦,那么怎么找到第一個相交節點呢?可以遍歷的過程中記錄鏈表的長度L1和L2(假設L1>L2)這是遍歷找到第一個鏈表中的第L1 - L2節點,然后鏈表一從第L1-L2個節點開始遍歷,鏈表二從第一個節點遍歷,每次前進一步,直到找到第一個相同的節點,則可以認為兩個鏈表存在相交節點,並且該點即為第一個相交節點(原來這里寫錯了,感謝Ider指出這個錯誤)。這種解法的時間復雜度也是線性的,但是如果兩個鏈表長度相差不多時,時間復雜度還是不錯的。
到這里,我知道的幾種典型的解法就說完了。歡迎大神們提供新的思路!!
3.問題擴展:(思考)
baidu曾經出過這樣的一個筆試題目,歸根到底也是找到兩個鏈表是否存在相同的節點,但是數據量很大,即鏈表長度是上億的。想想那么應該怎么處理呢?
學習中的一點總結,歡迎拍磚哈^^