判断一个链表是否有环就有点像判断一个小数是否为循环小数。如:12.3756954756954...循环体为756954进入节点为7。如果我们从这个思路来考虑那就走远了。因为,可能这个循环体长度特别长,而这个循环体中又可能存在很多小的循环体。比如:1.1235353512791235353512791...如果我们把35当做循环体那就错了,循环体应该是123535351279。正确的思路应该是参照环形赛道跑步比赛。试想,如果赛道没有环那么即使跑无限长的时间。跑得慢的人永远看不见跑得快的。而如果是环形赛道,只要时间足够长,跑得快的迟早会追上跑的慢的。
参考:
环形追及问题公式:n=(v1-v2)*t
n:相遇时圈数,v1:1节点速度,v2:2节点速度,t:时间
作为单向链表,设两个指针 fast,slow,假设fast走2步,slow走1步。
那么
ListNode slow = h;
ListNode fast = h;
while(fast != null && fast.next != null ){//如果跳出循环证明连表是单向的
slow = slow.next;//1步
fast = fast.next.next;//2步
if(slow == fast){//快的追上慢的
return true;
}
}
return false;
如果要知道到底从哪个节点进入循环的呢?
假设相遇点到入环点的距离为x,入环段长度为a,环长r,假设相遇时fast已绕环k圈,slow已绕环n圈,(n∈N+,k∈N+且k>=n),如果fast速度是2倍于slow速度。那么n>1则k必然大于2,也就是说slow在环上每个位置都出现了一次以上了。那么fast肯定于slow相遇过(如果fast >2slow的情况不确定。)那么kr+x+a = 2(nr + x + a)此时令n=0,并且k <= 2n所以k=0
整理 r= a + x;也就是说此时碰撞点离入环点还差a,此时如果设新的两个指针,p,q同样速度,一个从起点,一个从碰撞点走,当他们下次碰撞的时候一定是入环点。
if(h == null || h.next == null) {
return null;
}
ListNode slow = h;
ListNode fast = h;
while(fast != null && fast.next != null ){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
ListNode p=h;
ListNode q=slow;
while(p != q){
p = p.next;
q = q.next;
}
return q;
}
}
return null;
返回的q则是进入循环的节点