双指针
- 先判断是否存在圆环
- 若存在圆环,让一个变量从起始点与剩下的这个点一起走,最终会相遇即找到入环扣。
设有快慢指针fast,slow。fast每次走两步,slow走一步。
当它们相遇fast走了 l + a ∗ c + k l+a*c+k l+a∗c+k,slow走了 l + b ∗ c + k l+b*c+k l+b∗c+k( l l l是链表开头至入环口的长度, c c c是环的长度, a , b a,b a,b是环的圈数, k k k是从入环口到相遇位置的长度)。
已知fast走过的距离是slow的两倍,所以通过运算可以得到, l = ( b − 2 a ) ∗ c − k l=(b-2a)*c-k l=(b−2a)∗c−k。
设一个新的指针从链表开头走,每次走一步,走 l l l步也就是 ( b − 2 a ) ∗ c − k (b-2a)*c-k (b−2a)∗c−k步,会走到入环口。
slow也继续向前走,走 ( b − 2 a ) ∗ c − k (b-2a)*c-k (b−2a)∗c−k步也会来到入环口。为什么?
- 解释一
首先 l l l是非负数, k k k是非负数,即 b − 2 c b-2c b−2c大于等于0。slow走 ( b − 2 a ) ∗ c (b-2a)*c (b−2a)∗c步意味着原地踏步了 b − 2 a b-2a b−2a圈,走 − k -k −k步就相当于倒退 k k k步,因为 k k k表示的是从入环口到相遇位置的长度,所以倒退 k k k步意味着slow回到了入环口。 - 解释二
前面分析得 l = ( b − 2 a ) ∗ c − k l=(b-2a)*c-k l=(b−2a)∗c−k,也即 l + k = ( b − 2 a ) ∗ c l+k=(b-2a)*c l+k=(b−2a)∗c。当fast从head出发,走l步会来到环入口处,而low走l步加上相遇之前得k步等效于从入口处出发走了(b-2a)*c步,即low回到了环入口处。
fast,slow的起点必须得是相同的,这样才能保证fast走过的长度是slow的2倍,否则不满足公式 l = ( b − 2 a ) ∗ c − k l=(b-2a)*c-k l=(b−2a)∗c−k
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;//fast,slow的起点必须得是相同的。
ListNode slow = head;
while(true){
if(fast == null || fast.next == null){
return null;
}
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
break;
}
}
fast = head;
while(slow != fast){
slow = slow.next;
fast = fast.next;
}
return fast;
}
}
下面这个代码不能通过,只是两个注释之间的代码顺序不一样,break不是跳出循环了吗?
自问自答:我明白了,if(fast == slow){break;}
在前面得话,一开始就会跳出循环,因为low和fast的起点是一样得。
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while(true){
if(fast == null || fast.next == null){
return null;
}
//两个注释之间的代码顺序不一样
if(fast == slow){
break;
}
fast = fast.next.next;
slow = slow.next;
//两个注释之间的代码顺序不一样
}
fast = head;
while(slow != fast){
slow = slow.next;
fast = fast.next;
}
return fast;
}
}