图解 快慢指针判断链表是否有环 , 快慢指针 寻找有环链表入口 经典算法题

7 篇文章 0 订阅
1 篇文章 0 订阅

题目如下:

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

工具代码

链表节点

class ListNode{
    public $val = null;
    /**
     * @var ListNode $next
     */
    public $next = null;

    function __construct($val = 0) {
        $this->val = $val;
        $this->next = null;
    }
}

空白方法

/**
 * Definition for a singly-linked list.
 * class ListNode {
 *     public $val = 0;
 *     public $next = null;
 *     function __construct($val) { $this->val = $val; }
 * }
 */

class Solution {
    /**
     * @param ListNode $head
     * @return ListNode
     */
    function detectCycle($head) {
        
    }
}

思路

快慢指针$slow同时出发,快指针$faster一次走两步, $slow一次走一步. 设在 M M M点相遇时$slow走了 S S S步. 则快指针$faster走了 2 S 2S 2S步.
快指针$faster先走了 S S S步到相遇点然后在圈里绕了 S S S步. 相遇则有环(如果是无环则$faster会先跑到尽头)
在这里插入图片描述

问个zz问题, 不想看的话请调到下一张图的位置
就在就在$slow和$fast相遇时, 有两个$sleepSlow   $sleepFast分别以与$slow   $fast相同的速度 同时从起点出发(跟风), 那么它们会相遇吗?
$slow和$fast继续以不变的速度绕圈四个指针有可能同时相遇吗? 相遇的点是哪里?
S S S 2 S 2S 2S步步都会在 M M M点, 那么 3 S 3S 3S 4 S 4S 4S步呢?
当然都是在 M M M
$sleepSlow和$sleepFast在初次相遇时分别走了 S S S步和 2 S 2S 2S步. 花了N长时间
$slow和$fast分别走了 2 S 2S 2S步和 4 S 4S 4S步, 也回到了 M M M点. 花了2N长的时间.

那么请问$slow和$sleepSlow速度都相同, 而且会在 M M M点相遇, 请问它们第一次在哪里相遇.
有个美女以一定速度在操场绕圈, 你看费尽心思 准时机以同样的速度从入口冲进去和一起跑. 那么请问你们第一次相遇时哪个点?当然是入口啊

把图画成这样肯定是因为我不在乎他们分别走了多少圈, 肯定不是我没法画得好看[狗头]

这里别人有画好看的图 .我就不剽窃了 https://mp.weixin.qq.com/s/Nh6jxQtO-xOT_WuX-B5w3Q

在这里插入图片描述

代码

    /**
     * @param ListNode $head
     * @return ListNode
     */
    function detectCycle($head) {
        if (!$head){
            return null;
            //空的
        }
        $sleepFastS = $sleepSlowS = $fastS = $slowS = 0;
        $sleepFast = $sleepSlow = $slow = $fast = $head;
        while (true){
            if (!$fast = $fast->next){
                return null;
                //无环
            }
            if (!$fast = $fast->next){
                return null;
                //无环
            }
            $slow = $slow->next;
            $slowS += 1;
            $fastS += 2;
            if ($slow === $fast){
                break;
                //相遇则有环
                //设$slow走了S步,
                //那么$fast走了2S步
            }
        }
        $res = $head;
        //在$fast和$slow初次相遇时 $sleepSlow $sleepFast出发
        //四个指针必定同时到达 $fast和$slow初次相遇的点
        while (true){
            $sleepFast = $sleepFast->next->next;
            $fast = $fast->next->next;
            $sleepSlow = $sleepSlow->next;
            $slow = $slow->next;

            $sleepSlowS += 1;
            $sleepFastS += 2;
            $slowS += 1;
            $fastS += 2;
            if ($sleepSlow === $slow){
                $res = $slow;//时机
            }
            if ($sleepFast === $sleepSlow){//四目相对
                break;
                // $sleepSlow $sleepFast 走的路程 和前一个循环$slow $fast走的一模一样
                // 现在$sleepSlow走了S步,
                // 那么$sleepFast走了2S步,
                // $slow走了2S步,
                // $fast走了4S步
            }
        }
        return $res;
    }

简化代码

    /**
     * @param ListNode $head
     * @return ListNode
     */
    function detectCycle($head) {
        if (!$head){
            return null;
        }
        $slow = $fast = $head;
        while (true){
            if (!$fast = $fast->next){
                return null;
            }
            if (!$fast = $fast->next){
                return null;
                //无环
            }
            $slow = $slow->next;
            if ($slow === $fast){
                break;
            }
        }

        $sleepSlow = $head;
        while ($sleepSlow !== $slow){
            $sleepSlow = $sleepSlow->next->next;
            $slow = $slow->next;
        }
        return $slow;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值