LeetCode-142. Linked List Cycle II(詳細證明)
這道題目是141. Linked List Cycle的加成版https://leetcode.com/problems/linked-list-cycle/,思路都是“龜兔賽跑追及問題必相遇”,但這條需要確定環的入口節點。我們需要定量化一下:
pSlow是慢指標,pFast為2倍速指標
當pSlow經過i(i=4)“單位步長”首次到達Entry節點時,pFast已經走了8(2i=8)單位步長(如圖)
假設某個時刻相遇在Hit節點,假設順時針方向Entry節點到Hit節點有b單位步長,Hit到Entry節點有c單位步長,環的長度為L=b+c,pSlow走過的單位步長為i,pSlow在環中走了 圈, ,因為運動時間相同速度二倍的關係pFast走過的步長為2i, pFast在環中走了 圈, ,則有下式:假設某個時刻相遇在Hit節點,假設順時針方向Entry節點到Hit節點有b單位步長,Hit到Entry節點有c單位步長,環的長度為L=b+c,pSlow走過的單位步長為i,pSlow在環中走了 圈, ,因為運動時間相同速度二倍的關係pFast走過的步長為2i, pFast在環中走了 圈, ,則有下式:
程式碼如下:
public class Solution { public ListNode detectCycle(ListNode head) { if(head==null || head.next==null) return null; if(head.next==head) return head; ListNode pFast=head; ListNode pSlow=head; while(true) { if(pFast.next==null || pFast.next.next==null) return null; pFast=pFast.next.next; pSlow=pSlow.next; if(pFast==pSlow) { pSlow=head; while(pFast!=pSlow) { pFast=pFast.next; pSlow=pSlow.next; } return pFast; } } } } }
速度還是蠻快的。
在wikipedia上查了cycle detection(https://en.wikipedia.org/wiki/Cycle_detection)的問題裡面的理論闡釋蠻多的。wiki介紹了還有一個Brent演算法,程式碼如下:
public class Solution {
public static ListNode detectCycle(ListNode head){
if(head==null || head.next==null)
return null;
int power=1;
int lambda=1;
ListNode pSlow=head;
ListNode pFast=pSlow.next;
while(pSlow!=pFast)
{
if(power==lambda)
{
pSlow=pFast;
power*=2;
lambda=0;
}
if(pFast==null)
return null;
pFast=pFast.next;
lambda+=1;
}
pSlow=head;
pFast=head;
for(int i = 0;i < lambda;i++)
pFast = pFast.next;
while(pSlow != pFast)
{
pSlow = pSlow.next;
pFast = pFast.next;
}
return pSlow;
}
}
可恥的翻譯了一段wiki原文(勿噴):
2倍速指標的妙處!!!
最早遇到這個問題是在刷LeetCode-287. Find the Duplicate Number:https://leetcode.com/problems/find-the-duplicate-number/看到了有趣的龜兔賽跑的演算法,感覺驚為天人的tricks,這大概就是演算法之美吧,美得令人陶醉,就是樸素的讓人看到思路就想去寫出程式碼的額衝動!
那個題目將指標換成了陣列索引,但思路就是雙指標追及,程式碼如下:
class Solution {
public int findDuplicate(int[] nums) {
int pFast=0,pSlow=0;
while(true)
{
pSlow=nums[pSlow];
pFast=nums[nums[pFast]];
if(pSlow==pFast)
{
pFast=0;
while(pFast!=pSlow)
{
pFast=nums[pFast];
pSlow=nums[pSlow];
}
return pFast;
}
}
}
}