1. 程式人生 > >判斷連結串列是否有環,如果有返回入環的第一個節點。

判斷連結串列是否有環,如果有返回入環的第一個節點。

如何判斷連結串列有環,這個問題很簡單,有環的連結串列,在遍歷的時候會永遠在環裡轉下去。

但如何返回入環的第一個節點,當然最簡單的思路是用額外的空間記錄是否訪問過該節點,如果訪問過,就立刻停止遍歷,並返回。

在《程式設計師程式碼面試指南》中,作者採用了兩個指標的方法,具體如下。
1設定一個slow指標和一個fast指標,開始時,slow和fast指向連結串列的頭,然後slow每次跳一步,fast每次跳兩步。
2如果連結串列無環,那麼fast一定先到終點,直接返回null。
3,如果有環,那麼fast和slow一定會再次相遇,當fast和slow相遇的時候,讓fast重新回到head的位置,slow不動。接下來,fast指標從每次移動兩步改為移動一步,slow依然每次只移動一步。
4 fast指標和slow一定會再次相遇,並且在第一個入環的節點處相遇,證明略。
思路很簡單,先是一個每次跳2一個每次跳1,相遇後,把跳2的那個指向head,然後兩個每次都只跳1,最後相遇的地方就是要找的地方。
但是這一個證明略。。。

那就自己證明一下。
設入環前需要走m步,環中有n個節點。
並設fast和slow相遇時slow總共走過S,
則有
(S-m)%n = (2S-m)%n (這裡是指在環中的位置)
這個公式說明什麼 -> S%n = 0
所以把fast放回表頭, 然後一步一步走,等他們下次相遇。
注意,他們下次相遇一定是重新從頭走的fast指標,走m步正好入環的時候相遇,不可能是在環裡走了幾圈才相遇,他們走的步長一樣,所以肯定是一入環就相遇。
我們要證明的是什麼:
就是 fast 走m 意味著slow走了m。而slow原來的位置是(S-m)%n
所以m步以後,slow停留在(S-m)%n+m%n的位置,
由上面可以知道S%n = 0
所以,slow停留在入環的位置。
(為什麼會執行把fast放回去,是因為第一次相遇slow停留在(S-m)%n的位置,所以slow 如果再走m步,而S%n = 0,就可以回到環的起點。)