zoukankan      html  css  js  c++  java
  • [数据结构] 如何判断一个链表是否有环? 环的入口? 环的长度?

    练习算法的时候, 遇到这个问题, 感觉挺有意思的, 所以记录下来.

    在网上参考了这个答案:
    https://blog.csdn.net/thefutureisour/article/details/8174313

    但是后来又自己想了一下, 用自己的思维思考了一遍, 感觉更加清晰明了.

    问题是:

    1. 如何判断一个链表是否有环?
    2. 环的长度如何计算?
    3. 如何找到环的入口?

    我们先看图说话:

    假设链表长度为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的长度, 那么如何求 环的入口呢?

    有两种办法:

    1. 用链表的长度 L 减去C, 即可得到从出发点走几步,就到了 环的入口. 比如这里, 7-5=2, 走两步, 环的入口是Node3.
    2. 如果我们没有求出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必然在环的入口相遇.

  • 相关阅读:
    (zhuan) Paper Collection of Multi-Agent Reinforcement Learning (MARL)
    SalGAN: Visual saliency prediction with generative adversarial networks
    百善孝为先
    现代人为这个世界留下了什么?
    如人饮水,冷暖自知。
    移动端tap与click的区别 && 点透事件
    渐进增强与优雅降级 && css3中普通属性和前缀属性的书写顺序
    JavaScript中的垃圾回收机制与内存泄露
    http2.0之头部压缩
    状态码301和302的区别
  • 原文地址:https://www.cnblogs.com/notfresh/p/ringInLinklist.html
Copyright © 2011-2022 走看看