第一步:檢測鏈表是否有環。
方法還是比較多的,這里先講一個:快慢指針。
快慢指針的方法,就是讓兩個指針同時指向鏈表。在向后遍歷的時候,一個指針每次走兩步,稱為快指針;一個指針每次走一步,稱為慢指針。如果快慢指針相遇,則說明鏈表有環,否則無環。(后面證明。)
代碼實現如下:
// 如果有環,則返回快慢指針相遇點。如果沒環,則返回NULL。 position IsLoop(list l) { if (l == NULL) { printf("Invalid parameter for function IsLoop!\n"); exit(-1); } list fast, slow; fast = slow = l; while (fast != NULL && fast->next != NULL) { slow = slow->next; fast = fast->next->next; if (slow == fast) return slow; } return NULL; }
第二步:找到環的入口。
先畫張圖吧:
這是個有環的鏈表。
鏈表開頭與環的入口的距離為a,快慢指針相遇的地點距離環的入口為b,環的長度(節點的數量)為c。
那么我們可以知道,當快慢指針相遇的時候,快指針走的路程是慢指針的2倍。如果相遇的時候慢指針在環中走了m圈,快指針在環中已經走了n圈,那么必有n/m >= 2。
所以我們可以知道,相遇時,慢指針走的距離s1是a+mc+b,快指針走的距離s2是a+nc+b。
根據2*s1 = s2,我們有2(a+mc+b) = a+nc+b。
即a = (n-2m)c-b
n-2m是一個非負的常數。
(1)如果n-2m=0,又因為a和b也非負,所以a和b皆為0。意味着相遇點和入口重合,是鏈表的第一個結點。即環的入口就是鏈表的第一個結點。
(2)如果n-2m>0,設為N。則a=Nc-b=(N-1)c + c-b,這個式子意味着從鏈表開頭到環的入口的距離,等於從相遇點到入口的距離。而這個結論在(1)中的特殊情況也適用。
如果編程解決的話,就是讓兩個普通的指針(慢指針),一個指向鏈表的開頭,一個指向相遇點,然后開始往后走,直到它們相遇。而相遇點就是環的入口。