1. 程式人生 > >LeetCode-142. Linked List Cycle II(詳細證明)

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;
            }          
        }
    }
}