淺談雙指針技巧(二)—通過雙指針判斷鏈表成環問題
在上一篇文章(//www.cnblogs.com/jilodream/p/16666435.html)中,我們已經知道可以通過快慢指針,最終判斷一個單向鏈表是否成環。
一般在判斷存在環之後,還有一個經典的問題:
查找環的起點節點是哪裡呢
力扣 142. 環形鏈表 II (//leetcode.cn/problems/linked-list-cycle-ii/)
也就是查找到下圖中的這個位置
乍一看這個問題很難處理,除非像前文說的通過快取來判斷首次加入到快取中的節點外,並沒有什麼其他好的辦法。
如果直覺不能給我答案,我們往往採用數學的辦法:
如上圖,假設判斷有環後,慢指針執行的距離是N,則快指針的執行的距離是2N。他們相遇的點是meet點。我們可以理解2N-N,也就是快指針超過慢指針的距離,其實就是圍繞meet節點繞了x圈(x>=0)。
所以最終的結論就是:
N=慢指針走的距離=快指針超過慢指針的距離=圍繞meet節點走x圈
此時我們讓兩個指針(a、b)同時走上述的兩個N,也就是一個從head節點,一個從meet節點開始,同速,一直走N步,最終會同時到達meet節點。
而這個過程的最後一部分,也就是start節點到meet節點,兩者的路徑其實是重合的。因此這兩個指針(a、b)會首先在start節點相遇,然後同時走相同的一段路徑,並最終一同走到meet節點。
如下圖:
a指針移動距離=b指針移動距離=N-lastLen(lastLen表示從start節點到meet節點之間的最短距離+x*環的周長)
也就是:
a指針移動距離=b指針移動距離
所以我們在尋找環的起點start節點時,分為兩部分:
1、通過快慢指針找到二者的相遇點meet節點。
2、新定義兩個節點a、b,分別指向meet 節點和head節點。兩者按照相同的速度前進,第一次相遇的節點就是環的起始節點start
我們直接看程式碼:
1 public ListNode detectCycle(ListNode head) { 2 ListNode tempHead = head; 3 if (tempHead == null) { 4 return null; 5 } 6 ListNode low = tempHead; 7 ListNode fast = tempHead; 8 while (true) { 9 if (fast.next == null || fast.next.next == null) { 10 return null; 11 } 12 fast = fast.next.next; 13 low = low.next; 14 if (fast == low) { 15 break; 16 } 17 } 18 ListNode a = tempHead; 19 ListNode b = fast; 20 while (true) { 21 if (a == b) { 22 break; 23 } 24 a = a.next; 25 b = b.next; 26 } 27 return a; 28 }