练习算法的时候, 遇到这个问题, 感觉挺有意思的, 所以记录下来.
在网上参考了这个答案:
https://blog.csdn.net/thefutureisour/article/details/8174313
但是后来又自己想了一下, 用自己的思维思考了一遍, 感觉更加清晰明了.
问题是:
- 如何判断一个链表是否有环?
- 环的长度如何计算?
- 如何找到环的入口?
我们先看图说话:
假设链表长度为7, 我们用 L 表示, 设有两个指针, P1和P2, 速度分别为1, 2, (P1 一次走1个结点, P1 一次走2个结点), 同时从 Node1出发.
那么请问: 何时P2再次赶上P1呢? 这是一个很有意思的问题. 就像以前的运动会长跑跑圈一样, 快的总会追上慢的.
我们要明白一个道理:
无论 P1 转了几圈, P2 第一次追上 P1 的时候, P2 一定比 P1 多走一圈而已!!
我们设 时间为 X 时, P2追上P1, 有表达式:
P1走的距离 + 一圈的长度 = P2走的距离
即为:
X * 1 + L = X * 2
化简为:
X = L
L = 7, 所以, P1和P2从 Node1出发后又一次且是第一次在 Node1相遇, 因为刚好走了一圈, 一圈长度为7.
我们再来看一个复杂的情况:
假设链表不是整个环, 而是从中间某一点开始有环的. 那么好像一下复杂了不少, 一下子有点绕不过弯了呢! 我是这样子的, 所以我想了好一会.
我们先来看图.
但是道理还是一样的.
无论环的入口在哪里,
无论 P1 和P2 在环里转了几圈, P2 第一次追上 P1 的时候, P2 一定比 P1 多走一圈环的长度而已!!
我们设 时间为 X 时, P2追上P1, 有表达式:
P1走的距离 + 此时环一圈的长度(记为C) = P2走的距离
即为:
X * 1 + C = X * 2
化简为:
X = C
在这张图中, 我们知道 C=5, P1和P2在 Node6处相遇, 因为是从 Node1出发的, P1走了5个时间单位, 速度为1.
更加一般的情况是, C = (V2-V1)* X, V2代表P2的速度, V1代表P1的速度, 我们为了方便表示, 简单令其为2 和 1 罢了.
既然我们知道了C的长度, 那么如何求 环的入口呢?
有两种办法:
- 用链表的长度 L 减去C, 即可得到从出发点走几步,就到了 环的入口. 比如这里, 7-5=2, 走两步, 环的入口是Node3.
- 如果我们没有求出C的长度怎么玩呢?
我们知道, 环的入口, 其实也是链表的尾结点, 和Node1的距离, 是L-C, 因为环的长度为C, 链表总长度为L, 而这段不在环内.
同时, 我们记相遇点为 NodeX, NodeX到 环的入口, 也就是尾结点的距离, 为 L-C. ? 如何理解, 下面是证明步骤:
因为P1从Node1出发走了X个时间长度单位到NodeX, 而剩下的长度为L-X, 而C=X.
所以, 所以NodeX到环的入口的距离为 L-C,
如果此时 再让 另一个指针P3 以和P1相同的速度出发往前走, 那么, P1和P3必然在环的入口相遇.