為什麼用快慢指標檢測連結串列是否有環的時候,快指標的步長選擇的是2,而不是3,4,5?
阿新 • • 發佈:2019-02-17
上個月在eBay上海進行了二面,一共4輪,每輪45分鐘左右。感覺基礎的資料結構,演算法,Java語言的題目回答得還行,對於一些開放的Problem Solving型別的問題有點兒束手無策。自然,最後的結果也是被鄙視了。有些失落,不過最讓我失落的不是沒有回答好那些開放的題,而是被問到如標題所述的問題時,腦子一片空白。
在準備面試的過程中,就會發現,利用快慢指標檢測單向連結串列中是否存在環的答案遍地都是。而這些答案中幾乎一致地默認了設定快指標的步長為2。然後我們就記住了這些答案,信心滿滿地準備迎接面試。當然,大部分公司也就看你知不知道怎麼檢測出環,再深入一點兒就問你怎麼確定環的起始位置。但是再深入一點兒呢?為什麼快指標的步長非得是2,而不能是3,4,5,或者其他的呢?
這篇部落格就是解釋為什麼步長選擇的是2,同時提醒我自己知其然,而不知其所以然的代價。
問題:
- 給你一個單向連結串列,實現一個演算法,判斷這個連結串列中是否存在環;
- 如果存在環,返回環的起始結點;
- (如果這個時候你給出的實現是用的快慢指標,而且步長是2)步長選擇3,4,5,或者其它的可以解決以上兩個問題嗎?如果也可以,那麼為什麼你選擇的步長是2?
證明快慢指標在環中是可以相遇的:
證明這個問題?可能你會說,只要快慢兩個指標都進入環中,兩者有速度差,快指標就一定能夠追上慢指標。可是這兒“追上”有可能是兩種情況(注意,這兒討論的是一般情況,快指標步長k >= 2),一是恰好追上(也就是相遇了),二是追上並超過了慢指標。當然,這兩種情況都不妨礙我們判斷是否有環存在。但是對於問題2,網上給出的解法通常都以恰好相遇為前提,求出起始結點的(至於怎麼求,稍後會講到)。那既然這樣,我們就有必要證明無論快指標步長k為多少,快慢指標在環中都能恰好相遇。 證明開始: 首先,換個角度想問題。用一個步長為1的指標模擬遍歷這個單向連結串列的過程。如上圖所示,設單向連結串列的起始結點為X0(0是下標,可惜CSDN還不支援上下標輸入),環的起始結點為Xs。自然,遍歷到最後就是一遍遍的重複這個環。如果,我們把遍歷的項都不斷記錄到一個數組中的話(下標按次序遞增),最後就是一定長度的序列串的重複。例如, X0, X1, ... , Xs, Xs+1, ..., Xs+cl-1 Xs+cl, Xs+cl+1, ..., Xs+2*cl-1 ... cl (circle length)表示環的長度。 假設 j 是 cl 的整數倍,並且是 cl 整數倍中滿足 j > s 最小的那個數。對於任意的 k (k >= 2),我們考慮下標分別為 j 和 jk 的兩個位置 Xj 和 Xjk。可見,Xj 就是慢指標走了 j 步之後的位置,而 j > s 則保證了此時慢指標已經進入環中。同理,此時的快指標在位置 Xjk,必然也在環中。 因為 j 是 cl 的整數倍,我們可以把 Xjk 看成是一個指標從位置 Xj 開始,走了 (k - 1) 次的 j 步。而每走 j 步,在單向連結串列的環中,其實又回到了 Xj 位置,因為 j 是 cl 的整數倍。所以我們有,Xj = Xjk 。這樣,我們就證明了快慢指標肯定會恰好相遇的問題。為什麼快指標步長要為2呢?
如何確定環的起始結點?
- 當慢指標剛好進入環中,也就是慢指標走了 s 步之後,快指標走了 2*s 步,所以快指標在環中走了 2*s - s = s 步;
- 由於存在 s > cl 的情況,我們記快指標超出 Xs 的距離是 s % cl ;
- 此時,快指標需要追及慢指標的距離是 cl - s % cl;
- 因此,當慢指標在環中走了cl - s % cl 步後,快指標追上了慢指標;