今天面試被問住了,很慚愧啊,回來上網查了一下思路。自己寫了點程式。
1.如何判斷是否有環?如果有兩個頭結點指標,一個走的快,一個走的慢,那麼若干步以後,快的指標總會超過慢的指標一圈。
2.如何計算環的長度?第一次相遇(超一圈)時開始計數,第二次相遇時停止計數。
3.如何判斷環的進入點:碰撞點p到連接點的距離=頭指標到連接點的距離,因此,分別從碰撞點、頭指標開始走,相遇的那個點就是連接點。
為什麼呢?需要一個簡單的計算過程:
(1)當fast與slow相遇時,show肯定沒有走完鏈表,而fast已經在還裡走了n(n>= 1)圈。假設slow走了s步,那麼fast走了2s步。fast的步數還等於s走的加上環裡轉的n圈,所以有:
2s = s + nr。因此,s = nr。
(2)設整個鏈表長為L,入口據相遇點X,起點到入口的距離為a。因為slow指標並沒有走完一圈,所以:
a + x = s,帶入第一步的結果,有:a + x = nr = (n-1)r + r = (n-1)r + L - a;即:
a = (n-1)r + L -a -x;
這說明:從頭結點到入口的距離,等於轉了(n-1)圈以後,相遇點到入口的距離。因此,我們可以在鏈表頭、相遇點各設一個指標,每次各走一步,兩個指標必定相遇,且相遇第一點為環進入點。
也許大家有一個問題,就是為什麼“當fast與slow相遇時,show肯定沒有走完鏈表”。這個問題比較坑,我也沒有找到很好的證明。不過大家自己畫幾個試試,會發現的確是這樣。
4.如何判斷兩個鏈表(不帶環)是否相交?將其中的一個鏈表首尾相連,然後判斷另一個鏈表是否帶環即可。這個比較簡單,程式就省略了。
#include <stdio.h>typedef struct Node{int val;Node *next;}Node,*pNode;//判斷是否有環bool isLoop(pNode pHead){pNode fast = pHead;pNode slow = pHead;//如果無環,則fast先走到終點//當鏈表長度為奇數時,fast->Next為空白//當鏈表長度為偶數時,fast為空白while( fast != NULL && fast->next != NULL){fast = fast->next->next;slow = slow->next;//如果有環,則fast會超過slow一圈if(fast == slow){break;}}if(fast == NULL || fast->next == NULL )return false;elsereturn true;}//計算環的長度int loopLength(pNode pHead){if(isLoop(pHead) == false)return 0;pNode fast = pHead;pNode slow = pHead;int length = 0;bool begin = false;bool agian = false;while( fast != NULL && fast->next != NULL){fast = fast->next->next;slow = slow->next;//超兩圈後停止計數,挑出迴圈if(fast == slow && agian == true)break;//超一圈後開始計數if(fast == slow && agian == false){begin = true;agian = true;}//計數if(begin == true)++length;}return length;}//求出環的進入點Node* findLoopEntrance(pNode pHead){pNode fast = pHead;pNode slow = pHead;while( fast != NULL && fast->next != NULL){fast = fast->next->next;slow = slow->next;//如果有環,則fast會超過slow一圈if(fast == slow){break;}}if(fast == NULL || fast->next == NULL)return NULL;slow = pHead;while(slow != fast){slow = slow->next;fast = fast->next;}return slow;}