LCP142 LeetCode142. Linked List Cycle II

前言

这次要啃个硬骨头!!!
网上也有相当多的解题思路,我也看过,但是仍然觉得还有不足之处。当然,十分感谢那些解题思路,以及LeetCode网友 RainbowSecret 的解答。才有以下我的这篇梳理。

题目

Acceptance : 31.3% Difficulty : Medium

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Note : Do not modify the linked list.

Follow up :

Can you solve it without using extra space ?

TagsLinked ListTwo Pointers

题解

首先感谢简书作者 floodiu 对本题的解答。我也是看过他的解答之后才有的我写在他的解答之下的疑问,以及以下这篇思路梳理。

图

  • X 是链表的起点。
  • Y 是环的起点。
  • Z 是 fast 和 slow 首次相遇的地方(二者同时从X出发,slow 每次移动一步, fast 每次移动两步)
  • a,b,c 分别表示 XY(蓝色), YZ(红色), ZY(绿色)的长度。
    当 fast 和 slow 在 Z 点 首次相遇时:
  • fast 移动的距离是: a + b + c + b
  • slow 移动的距离是: a + b

毫无疑问, floodiu 的思路是正确但是不太全面的。 非常容易可见的错误是,当 a 的距离非常长之后, fast 指针将在 cycle 中转过好多圈,也就是 fast 走过的距离并非简单的 a + b + c + b。

除此之外,当我第一次看到这个问题时候的疑问是:

  • 怎么证明 fast 在 cycle 中 与 slow 站在同一位置时,他俩是第一次相遇,而非 fast 曾经超越 slow n 次 而后才又站到一起呢?

    这个问题的解答可以考虑这样一个问题: fast 首先进入 cycle 是事实,但是我们仍然可以把它看作是 fast 在 slow 之后。 也就是,当 slow 进入 cycle 时, 就变成了两个人在环形跑道上的追击问题了,把从这时起的位置看作是起始位置,可能 slow 在 fast 之前若干距离(记为α)处。但是他俩之间的相对速度是 -1, 也就是说,自然数 α 将通过不断地 α - - ,总会有一刻,它的值为 0,而非一下变成了负数。所以,他俩站上同一位置时,他俩可能真的是第一次相遇。这里我又想到了一种极特殊的情况,就是 slow 在XY上 Y - 1 的位置, fast 在环中ZY上 Y - 1 的位置。那么当 slow 进入环中的时候,fast 刚好超过了一下它进入了 YZ 上 Y + 1 的位置。当然,以这样讨论是不是 fast 超过了 slow,还需要深入考虑具体的情况。但是我们接下来分析的解题思路是与整个这个疑问都无关的!!

真·题解

OK,让我们进入正式的题目解析过程。感谢LeetCode网友 RainbowSecret 的解答。但是我看他的解答还是觉得不太十分清晰。或者,由于语言的问题,理解起来并不十分容易。

首先,上图
图

  • X 为链表起点,Y 为链表中环 cycle 的进入点Entry, Z 为 fast 指针 与 slow 指针 在环中相遇的位置。
  • XY 段长度(从X走到Y所需的步数,亦即所需=root->next的操作数)为 a, 弧YZ(红色)段长度为 b,弧ZY(绿色)的长度为 c。
  • 于是环的长度为 YZ + ZY 的长度,也就是 b + c,这里我们记为 d = b + c。
  • 假设他们相遇时 slow 需要走过 t 步。

以上为所有事实,及我们的变量声明。

首先,当他们相遇时,由于 fast 是 slow 速度的2倍,所以:
2 * t = t + N1 * d。。。。(N1 = 1, 2, 3……)。。。。。。。。。。。①
也就是说,fast 所走的距离 = slow 所走的距离(也就是 a + b) 再加上 fast 自己在环处转的圈。这里 N1 最小只能取到 1,因为当 fast 再次追上 slow 时,fast 无论如何都是要再跨过 一次 Y 点的。否则,fast 将一直处于 slow 之前。

把右侧的 t 移到左侧,于是得出:
t = N1 * d。。。。。。②
额,这个结果看上去令人有些惊讶。但,这是事实。

同样的,我们考虑 fast 指针走过的距离的话。相当于 a + N2 * d + b。
或者可以理解为,我们把第一个等式中的右侧的 T, 也就是 slow 走过的距离用 a + b 来代替。
所以有
2 * t = a + N2 * d + b。。。。。。(N2 = 1, 2, 3……)。。。。。③

③ - 2 * ② 得

(2 * N1 - N2 ) * d = a + b。。。。。。。④

又 ∵ d = b + c,把它代入 ④ 可得:

(2 * N1 - N2 - 1 ) * d + b + c = a + b

于是便有:
(2 * N1 - N2 - 1) * d + c = a

这等式可以理解为:
直线段XY的步数 等于 在cycle中转 n 圈的步数 加上 弧 ZY (绿色) 段的步数 c。

也就是 a = n * d + c

那么,接下来就可以分析了。

现在 fast 和 slow 相遇在 Z 点,把 slow 拨回到出发点 X, 并且 把 fast 的速度也降为 1 步 1 格。让两者同时出发。
那么, 当 slow 从 X 走到 Y 时,slow 走了个 a 步。
而现在的 fast 跟 slow 的速度是一致的, 它也将会走过 a 步。而刚好恰恰 a 又等于 n 圈加上 出发时 fast 距离 Y 点的距离 c。
所以,当 slow 走到 Y 点的时候, fast 也刚好 转过了 n 圈之后 又回到了 Y 点。

不知道这样的解释大家清楚了没有。

以下是AC的代码:

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if (head == NULL)
            return NULL;
        ListNode *fast = head, *slow = head;
        while (fast->next != NULL && fast->next->next != NULL)
        {
            fast = fast->next->next;
            slow = slow->next;
            if (fast == slow)     // 当 slow 和 fast 相遇时
            {
                slow = head;      // 把 slow 拨回到起点
                while (fast != slow)    // 直到 slow 和 fast 再次相遇
                {
                    fast = fast->next;  // 这次让 fast 同样一步一个脚印地走
                    slow = slow->next;
                }
                return fast;   // 当他们再次相遇,将相遇在环的入口 Y 处
            }
        }
        return NULL;        
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深海Enoch

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值