• [C/C++]数据结构 深入挖掘环形链表问题


    前言

            在上一篇文章中讲述了如何判断链表是否带环,在观看本片文章时建议先了解一下这篇文章的内容[C/C++]数据结构 链表OJ题:环形链表。本篇文章我们将讲述关于环形链表的几种不同的情况如下,同时我们要解决另一个环形链表问题----找到入环点

    1. slow一次走一步fast一次走两步一定会相遇吗?
    2. slow一次走一步fast一次走三部一定会相遇吗?
    3. slow一次走n步fast一次走m步一定会相遇吗? (m>n>1)

    情况分析:

    1.slow一次走一步fast一次走两步一定会相遇吗?

            这个问题在上一篇文章中我们也讲过,若slow指针已经入环,每走一步fast与slow之间的距离就会减一,减为0时两指针相遇

    2.slow一次走一步fast一次走三部一定会相遇吗?

            假设slow入环时,fast和slow之间距离为N

    之后每走一步,fast和slow之间的距离就会减小2,这里就需要讨论N的取值了

    • 当N为偶数, 距离变化: N -> N-2 -> N-4.......->2 -> 0 ,两指针距离一次减小2,一定会相遇
    • 当N为奇数, 距离变化: N -> N-2 -> N-4.......->1 -> -1

            距离变为-1就说明fast在走三步的时候跳过了slow,又跑到了slow的前面,第一轮追逐没有相遇,此时又要开时第二轮追逐,假设环的周长为c,开始第二轮追逐时fast与slow之间的距离为c-1

    此时有需要讨论c的取值

    • 当c为奇数,c-1为偶数,两指针一定会相遇
    • 当c为偶数,c-1为奇数,slow与fast就又不会相遇,同理这种情况无论第几次追逐两指针都不会相遇

    总结:

    1. 如果N为偶数,两指针在第一轮追逐就会相遇
    2. 如果N为奇数,c为奇数,第一轮两指针会错过,但是在第二轮会相遇
    3. 如果N为奇数,为偶数,则两指针永远不会相遇

    问题来了: 如果N为奇数,为偶数,则两指针永远不会相遇,但是这个条件成立吗?如何验证

    slow入环时:

    慢指针走的路程: L

    快指针走的路程: n*c - N  (n代表走了n圈)

    快指针所走路程为慢指针所走路程的三倍,可得:

    3L = L+ n*c - N 即 2L = n*c - N

    分析一下这个公式:

    2L一定为偶数,n*c也一定为偶数,但是n*c-N一定为奇数,因为在条件N就为奇数,

    所以我们推出来的公式是错的,也就是说如果N为奇数,为偶数,则两指针永远不会相遇这个条件不成立,即不会出永远追不上的情况,所以不论什么情况两指针都会在某一轮的追逐中相遇

    3.slow一次走n步fast一次走m步一定会相遇吗? (m>n>1)

            这种情况和第二种情况分析过程类似,假设slow入环时,fast和slow之间距离为N,每走一步两者距离减(m-n),当N%(m-n)时,两指针便可相遇,由于这类问题给出实际值时才有意义,所以就不对其过多分析了


    接下来我们解决最后一个问题:

    如何找到链表的如环点?

    题目描述:

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

    如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

    不允许修改 链表。

    分析:

    这个题还是采用快慢指针的方法,slow一次走一步,fast一次走两步

    由于fast速度是slow的二倍,所以相遇时,fast所走路程为slow的两倍,即

    2*(L+x) = L+n*c+x

    化简为: L = n*c - x

    由这个公式就可以推出:

    一个指针从头开始走,一个指针从相遇点开始走,他们会在入环点相遇

    有了这个理论,这个题就可以迎刃而解了,我们先找出相遇点(如何找相遇点在前言所提文章中讲过),在让两指针,一个从头开始走,一个从相遇点开始走,判断哪个点符合这个公式

    代码:

    1. struct ListNode *detectCycle(struct ListNode *head) {
    2. struct ListNode *slow=head;
    3. struct ListNode *fast=head;
    4. while(fast&&fast->next)
    5. {
    6. slow=slow->next;
    7. fast=fast->next->next;
    8. if(slow==fast)//找相遇点
    9. {
    10. struct ListNode* meet=slow;//相遇点
    11. while(meet!=head)
    12. {
    13. head=head->next;
    14. meet=meet->next;
    15. }
    16. return meet;
    17. }
    18. }
    19. return NULL;
    20. }

  • 相关阅读:
    vue组件传值
    Python 字符串详解
    WinUI(WASDK)使用ChatGPT和摄像头手势识别结合TTS让机器人更智能
    由一个项目引起智能工厂及数字工厂的灵魂拷问
    Linux下gz和tar.gz、与Windows天zip压缩解压
    @RequestParam注解的详细介绍
    find 查找 Bazel 构建覆盖率文件的一个☝️坑
    springboot的基本配置
    【Linux篇】第十一篇——动静态库(动静态库的介绍+动静态库的打包与使用)
    辐射威胁:揭示辐射对人体健康和肠道菌群的影响及防护
  • 原文地址:https://blog.csdn.net/m0_74910646/article/details/134337926