【摘要】有一個單鏈表,其中可能有一個環,也就是某個節點的next指向的是鏈表中在它以前的節點,這樣在鏈表的尾部造成一環。oop
1、如何判斷一個鏈表是不 是這類鏈表?spa
2、若是鏈表爲存在環,若是找到環的入口點?指針
擴展:判斷兩個單鏈表是否相交,若是相交,給出相交的第一個點。it
1).有一個單鏈表,其中可能有一個環,也就是某個節點的next指向的是鏈表中在它以前的節點,這樣在鏈表的尾部造成一環。
問題:
1、如何判斷一個鏈表是否是這類鏈表?
2、若是鏈表爲存在環,若是找到環的入口點?
解答:
1、判斷鏈表是否存在環,辦法爲:
設置兩個指針(fast, slow),初始值都指向頭,slow每次前進一步,fast每次前進二步,若是鏈表存在環,則fast一定先進入環,而slow後進入環,兩個指針一定相遇。(固然,fast先行頭到尾部爲NULL,則爲無環鏈表)程序以下:
bool IsExitsLoop(slist * head)
{
slist * slow = head , * fast = head;
while ( fast && fast -> next )
{
slow = slow -> next;
fast = fast -> next -> next;
if ( slow == fast ) break ;
}
return ! (fast == NULL || fast -> next == NULL);
}
2、找到環的入口點
當fast若與slow相遇時,slow確定沒有走遍歷完鏈表,而fast已經在環內循環了n圈ast
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;擴展
fast = head; //slow = head;
while (slow != fast)
{
slow = slow -> next;
fast = fast -> next;
}
return fast;
}
附一種易於理解的解釋:
一種O(n)的辦法就是(搞兩個指針,一個每次遞增一步,一個每次遞增兩步,若是有環的話二者必然重合,反之亦然):
關於這個解法最形象的比喻就是在操場當中跑步,速度快的會把速度慢的扣圈
能夠證實,p2追遇上p1的時候,p1必定尚未走完一遍環路,p2也不會跨越p1多圈才追上
咱們能夠從p2和p1的位置差距來證實,p2必定會遇上p1可是不會跳過p1的
由於p2每次走2步,而p1走一步,因此他們之間的差距是一步一步的縮小,4,3,2,1,0 到0的時候就重合了
根據這個方式,能夠證實,p2每次走三步以上,並不總能加快檢測的速度,反而有可能判別不出有環
既然可以判斷出是不是有環路,那改如何找到這個環路的入口呢?
解法以下: 當p2按照每次2步,p1每次一步的方式走,發現p2和p1重合,肯定了單向鏈表有環路了
接下來,讓p2回到鏈表的頭部,從新走,每次步長不是走2了,而是走1,那麼當p1和p2再次相遇的時候,就是環路的入口了。
這點能夠證實的:
在p2和p1第一次相遇的時候,假定p1走了n步驟,環路的入口是在p步的時候通過的,那麼有
p1走的路徑:p+c =n; c爲p1和p2相交點,距離環路入口的距離
p2走的路徑:p+c+k*L = 2*n; L爲環路的周長,k是整數
顯然,若是從p+c點開始,p1再走n步驟的話,還能夠回到p+c這個點
同時p2從頭開始走的話,通過n步,也會達到p+c這點
顯然在這個步驟當中p1和p2只有前p步驟走的路徑不一樣,因此當p1和p2再次重合的時候,必然是在鏈表的環路入口點上。
擴展問題:
判斷兩個單鏈表是否相交,若是相交,給出相交的第一個點(兩個鏈表都不存在環)。
比較好的方法有兩個:
1、將其中一個鏈表首尾相連,檢測另一個鏈表是否存在環,若是存在,則兩個鏈表相交,而檢測出來的依賴環入口即爲相交的第一個點。
2、若是兩個鏈表相交,那個兩個鏈表從相交點到鏈表結束都是相同的節點,咱們能夠先遍歷一個鏈表,直到尾部,再遍歷另一個鏈表,若是也能夠走到一樣的結尾點,則兩個鏈表相交。
這時咱們記下兩個鏈表length,再遍歷一次,長鏈表節點先出發前進(lengthMax-lengthMin)步,以後兩個鏈表同時前進,每次一步,相遇的第一點即爲兩個鏈表相交的第一個點。循環
鏈表相交必有相同的結束節點。遍歷