问题:如何检测一个链表是否有环呢?如果有,那么怎么确定环的起点?如何确定环的长度?
- 时间复杂度:O(n)
- 空间复杂度:O(1)
好比两个人在赛跑,A速度快,B速度慢,经过一定时间后,A总会和B相遇,且相遇时,A跑过的总距离减B跑过的总距离一定是圈长的n倍。
Floyd使用两个指针,一个慢(龟)指针每次前进一步,一个快指针(兔)每次前进两步(两步或多步效果是等价的,只要一个比另一个快就行。但是随着移动步数的增加,算法的复杂度可能增加)。如果两者在链表头以外(不包含开始情况)的某一点相遇(即相等),那么说明这个链表就是有环的,否则,如果(快慢指针)到达了链表尾部(果然存在结尾,肯定无环),那么无环。
假设一个链表是下面这样一个:
设环长为L,非环形部分长度为m,相遇处离环入口为距离k,当第一次相遇时,显然慢指针slow可能已经走了m+aL+k(a=0,1,2,…),a为慢指针走过的环圈数(如果当环很大的时候,可能好几圈才能相遇),然而快指针fast走了m+bL+k的距离。
一开始已经说明快指针fast的速度为慢指针slow的两倍,所以,上述的距离还存在这样的关系。
接着将慢指针重新放置到起点出发,快指针从相遇点出发,如上图,让快慢指针每次都走一步,它们再次相遇的点就是环的入口。
代码实现:
class ListNode{
public int data;
ListNode next;
}
public class TestSolution {
ListNode listNode=new ListNode();
public ListNode findBeginLoop(ListNode head){
ListNode slowPtr=head;
ListNode fastPtr=head;
boolean loopExists=false;
if(head==null){
return head;
}
while(slowPtr.next!=null&&fastPtr.next.next!=null){
slowPtr=slowPtr.next;
fastPtr=slowPtr.next.next;
if(slowPtr==fastPtr){
loopExists=true;
break;
}
}
if(loopExists){//环存在
slowPtr=head;
while(slowPtr!=fastPtr){
slowPtr=slowPtr.next;
fastPtr=fastPtr.next;
}
return slowPtr;
}
return null;//环不存在
}
}
如果需要求环的长度,只需要固定一个指针,让另一个指针继续在环中行走,同时计数count,再次相遇count即为环长!
文章内容主要参考了以下博客,做了少许改动,感谢前辈们的付出和指导!
https://blog.csdn.net/u012534831/article/details/74231581