定理: 碰撞点p到连接点的距离 = 头指针到连接点的距离。
因此,分别从碰撞点、头指针开始走,相遇的那个点就是连接点。
则总长度(也是总结点数)为 a+b 。
从头开始,0 base 编号。
将第 i 步访问的结点用 S(i) 表示。i = 0, 1 ...
当 i<a 时, S(i)=i ;
当 i>=a 时,S(i)=a+(i-a)%b 。
分析追赶过程:
两个指针分别前进,假定经过 x 步后,碰撞。则有:S(x)=S(2x)
由环的周期性有:2x=tb+x 。得到 x=tb 。
另,碰撞时,必须在环内,不可能在甩尾段,有 x>=a 。
连接点为从起点走 a 步,即 S(a) (S(a)为环的入口)。
S(a) = S(tb+a) = S(x+a)。
得到结论:从碰撞点 x 前进 a 步即为连接点。
根据假设易知 S(a-1) 在甩尾段,S(a) 在环上,而 S(x+a) 必然在环上。所以可以发生碰撞。
而,同为前进 a 步,同为连接点,所以必然发生碰撞。
因此,分别从碰撞点、头指针开始走,相遇的那个点就是连接点。
定理的证明与分析:
则总长度(也是总结点数)为 a+b 。
从头开始,0 base 编号。
将第 i 步访问的结点用 S(i) 表示。i = 0, 1 ...
当 i<a 时, S(i)=i ;
当 i>=a 时,S(i)=a+(i-a)%b 。
分析追赶过程:
两个指针分别前进,假定经过 x 步后,碰撞。则有:S(x)=S(2x)
由环的周期性有:2x=tb+x 。得到 x=tb 。
另,碰撞时,必须在环内,不可能在甩尾段,有 x>=a 。
连接点为从起点走 a 步,即 S(a) (S(a)为环的入口)。
S(a) = S(tb+a) = S(x+a)。
得到结论:从碰撞点 x 前进 a 步即为连接点。
根据假设易知 S(a-1) 在甩尾段,S(a) 在环上,而 S(x+a) 必然在环上。所以可以发生碰撞。
而,同为前进 a 步,同为连接点,所以必然发生碰撞。
综上,从 x 点和从起点同步前进,第一个碰撞点就是连接点。
/*********************************************************************
* 函数名称:linklist *IsLoop(linklist *head)
* 函数功能:判断链表是否含有环,并找出环的入口
* 参 数:head----链表的头结点
* 返 回 值:若有环,则返回链表中环的入口结点;否则返回NULL
* 说 明:程序参考定理:碰撞点p到连接点的距离=头指针到连接点的距离
*********************************************************************/
linklist *IsLoop(linklist *head)
{
linklist *p1=head, *p2=head;
int IsLoopFlag=0;
if(head==NULL)
return NULL;
while(p2->next!=NULL && p2->next->next!=NULL)
{
p1 = p1->next;
p2 = p2->next->next;
if(p1==p2)
{
IsLoopFlag = 1;
break;
}
}
if(!IsLoopFlag)
return NULL;
else
{
// p2指向头结点,p1在第一次相遇点。
// 之后利用定理,当p1再次等于p2时,相遇点即为环的入口结点
p2 = head;
while(p1!=p2)
{
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
}