00双指针中等 leetcode142. 环形链表 II

双指针

  • 先判断是否存在圆环
  • 若存在圆环,让一个变量从起始点与剩下的这个点一起走,最终会相遇即找到入环扣。

设有快慢指针fast,slow。fast每次走两步,slow走一步。

当它们相遇fast走了 l + a ∗ c + k l+a*c+k l+ac+k,slow走了 l + b ∗ c + k l+b*c+k l+bc+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=(b2a)ck

设一个新的指针从链表开头走,每次走一步,走 l l l步也就是 ( b − 2 a ) ∗ c − k (b-2a)*c-k (b2a)ck步,会走到入环口。

slow也继续向前走,走 ( b − 2 a ) ∗ c − k (b-2a)*c-k (b2a)ck步也会来到入环口。为什么?

  • 解释一
    首先 l l l是非负数, k k k是非负数,即 b − 2 c b-2c b2c大于等于0。slow走 ( b − 2 a ) ∗ c (b-2a)*c (b2a)c步意味着原地踏步了 b − 2 a b-2a b2a圈,走 − 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=(b2a)ck,也即 l + k = ( b − 2 a ) ∗ c l+k=(b-2a)*c l+k=(b2a)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=(b2a)ck

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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值