上次写了关于寻找环开始的解法,上网了搜了一下解法,发现原来这题是有故事的。具体大家可以去搜一搜,是个英文的blog。
代码就没有什么说的,如果知道了解法。代码是非常简单的。
大概的说一下解法,
1):定义两个指针S,F分别对应每次走一步,两步指针。就是快慢指针。初始都指向头结点。
2):当有环时,S==F。将其中一个指向(比如S)head,同时S和F相同的速度向后移动,当S==F时停止。
3):S==F就是环的起始结点。
关键是推导的过程。讨论下一般的情况。当S走到起始结点时,F走到n(环的长度)的第k个位置。假设S和F会在环的第n-x个结点相遇。我们就来求这个x。
1):当S走n/2步时,F走了n步。那么F还在k的位置上。
2):现在我们要求S走到n-x的位置上,那么S还需要走(n-x)-n/2步,我们就让S走(n-x)-n/2=n/2-x步。这个时候F走了2*(n/2-x)距离,即n-2x。所以F到了n-2x+k位置。
3):我们要求F也走到n-x的位置上,所以有n-2x+k=n-x。所以有了x==k的结论!!!
下面就简单了,因为F在第k位置上的时候,S走的是F的一半,S为换起点。所以从head到起始结点距离==k。
所以就有了上面的解法。
因为有环,所以位置对环大小取余后还是相对的位置,上面说的k,n-x都是相对于环n长度的。
放几张图吧,是别人blog的图。
图一
图二
图三
图四
图片来自(http://umairsaeed.com/2011/06/23/finding-the-start-of-a-loop-in-a-circular-linked-list/)
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
// IMPORTANT: Please reset any member data you declared, as
// the same Solution instance will be reused for each test case.
if(!head)
return NULL;
if(head->next==head)
return head;
ListNode *pre,*p;
pre=p=head;
do{
if(p && p->next && p->next->next)
{
p=p->next->next;
}
else
return NULL;
pre=pre->next;
}while(pre!=p);
pre=head;
while(pre!=p)
{
pre=pre->next;
p=p->next;
}
return pre;
}
};
如果有不对的地方,还请指正下。也可以讨论下。