LeetCode-142. Linked List Cycle II(详细证明)与龟兔赛跑算法

这道题目是141. Linked List Cycle的加成版https://leetcode.com/problems/linked-list-cycle/,思路都是“龟兔赛跑追及问题必相遇”,但这条需要确定环的入口节点。我们需要定量化一下:

                 LeetCode-142. Linked List Cycle II(详细证明)与龟兔赛跑算法_第1张图片

pSlow是慢指针,pFast为2倍速指针

当pSlow经过i(i=4)“单位步长”首次到达Entry节点时,pFast已经走了8(2i=8)单位步长(如图)

                LeetCode-142. Linked List Cycle II(详细证明)与龟兔赛跑算法_第2张图片

假设某个时刻相遇在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在环中走了 圈, ,则有下式:

                                           LeetCode-142. Linked List Cycle II(详细证明)与龟兔赛跑算法_第3张图片

 

代码如下:

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原文(勿喷):

                                                        LeetCode-142. Linked List Cycle II(详细证明)与龟兔赛跑算法_第4张图片

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

 

 

你可能感兴趣的:(算法设计(Practice))