题目:设计一个算法完成以下功能:判断一个链表是否有环,如果有,找出环的入口点并返回。否则返回NULL
关键字:单链表+判断循环
思路
关注:
有环的充要条件:两个指针不停遍历,一定会相遇
算法的基本设计思想:
1.设置快慢两个指针分别为fast和slow,初始时都指向链表头head。
slow每次走一步,即slow=slow->next;
fast每次走两步,即fast比slow走得快,如果有环,fast一定会先进入环,而slow后进入环。
当两个指针都进入环后,经过若干次操作后两个指针定能在环上相遇。
这样就可以判断一个链表是否有环。
如下图所示,当slow刚进入环时,fast早已进入环。
因为fast每次比slow多走一步且fast与slow的距离小于环的长度,所以fast与slow相遇时,slow所走的距离不超过环的长度。
如下图所示,设头结点到环的入口点的距离为a, 环的入口点沿着环的方向到相遇点的距离为x,环长为r,相遇时fast绕过了n圈。(n=0,1,2,…)(上文已分析,slow走的距离一定等于X,总圈数一定小于等于一圈,不可能出现套圈情况)
相遇时:fast比slow多走的距离为:n个环长
则有(a+x)/1=(a+nr+x)/2。整理得:a=nr-x
路程/速度=时间
Q:如何将通过数学得到的等式化为算法呢?
A:回到指针遍历的实际场景中找寻规律,最终目的是要求返回环的入口点地址。
因此,最理想情况就是当经过一番操作之后,p指针可以刚好降临在环的入口点,此时返回p指针,就是最终结论。
而到达环的入口点,最常见的起点,即把链表第一个结点作为起点而言,刚好需要行走过a个位置
而数学公式告诉我们:a=n*r-x
显然:从头结点到环的入口点的距离
等于
n倍的环长减去环的入口点到相遇点的距离
所以,对于两个同步指针:
当一个完成从头结点0~a
可以带动
另一个完成从环入口点开始,移动n圈还差X个位置,最终到达(在环中)逆时针距离环入口x个位置的任务
即
让另另一个完成从环入口点顺时针后X个位置开始,完成n圈,最终刚好到达入口点的任务
因此可设置两个指针,一个指向head,一个指向相遇点(即从环入口点顺时针后X个位置),两个指针同步移动(均为一次走一步),相遇点即为环的入口点。
LNode*FindLoopStart(LNode*head){
LNode *fast=head,*slow=head;//设置快慢两个指针
while(slow!=NULL&&fast->next!=NULL){
slow=slow->next;//慢指针每次走一步
fast=fast->next->next;//快指针每次走两步
if(slow==fast) break;//相遇
}
if(slow==NULL||fast->next==NULL)
return NULL;//没有环,返回NULL
LNode*p1=head,*p2=slow;//确定有环开始找环的入口点。分别指向开始点、相遇点
while(p1!=p2){
p1=p1->next;
p2=p2->next;
}
return p1;//返回入口点
}