• 【LeetCode】经典的环形链表


    ​🌠 作者:@阿亮joy.
    🎆专栏:《阿亮爱刷题》
    🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根
    在这里插入图片描述




    👉环形链表👈

    给你一个链表的头节点 head ,判断链表中是否有环。

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

    如果链表中存在环 ,则返回 true 。 否则,返回 false 。


    示例 1:

    在这里插入图片描述
    输入:head = [3,2,0,-4], pos = 1
    输出:true
    解释:链表中有一个环,其尾部连接到第二个节点。

    示例 2:

    在这里插入图片描述
    输入:head = [1], pos = -1
    输出:false
    解释:链表中没有环。

    提示:

    • 链表中节点的数目范围是 [0, 104]
    • -105 <= Node.val <= 105
    • pos 为 -1 或者链表中的一个 有效索引 。

    思路:定义两个指针,分别为slowfastslowfast指向链表的开始,slow一次走一步,而fast一次走两步。如果链表不带环,fast将会为NULL;而如果链表带环的话,fast就会在环内跟slow相遇。这就是高中物理的相遇问题。以至于fastslow为什么会在环内相遇,待会再给大家证明。
    在这里插入图片描述

    /*
     * struct ListNode {
     *     int val;
     *     struct ListNode *next;
     * };
     */
    bool hasCycle(struct ListNode *head) 
    {
        struct ListNode* slow = head;
        struct ListNode* fast = head;
        while(fast && fast->next)//对应链表节点有奇数个或者偶数个的情况
        {
            slow = slow->next;
            fast = fast->next->next;
            if(slow == fast)//相遇表示链表带环
            {
                return true;
            } 
        }
        return false;//循环结束代表链表不带环
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    👉环形链表的延伸问题👈

    问题

    • 如果链表带环的话,按照slow一次走一步,fast一次走两步的走法,slowfast一定会在环内相遇吗?会不会在环内错过,永远遇不上?如果一定会相遇,请证明!
    • 为什么要slow一次走一步,fast一次走两步呢?那如果slow一次走一步,fast一次走n(n >= 3)步,slowfast还会不会一定在环内相遇?请证明!

    结论

    • 如果链表带环,按照slow一次走一步,fast一次走两步的走法,slowfast一定会在环内相遇,不会错过。
    • 如果按照slow一次走一步,fast一次走n(n >= 3)步的走法,slowfast不一定会在环内相遇。

    证明

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    👉环形链表II👈

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

    在这里插入图片描述
    输入:head = [3,2,0,-4], pos = 1
    输出:true
    解释:链表中有一个环,其尾部连接到第二个节点。

    在上面,已经给出了环形链表延伸问题的结论和证明,但是怎么求环的入口点呢?

    结论

    一个指针从相遇点开始走,另一个指针从头结点开始走,它们会在环的入口点相遇。

    证明

    在这里插入图片描述

    /*
     * struct ListNode {
     *     int val;
     *     struct ListNode *next;
     * };
     */
    struct ListNode *detectCycle(struct ListNode *head) 
    {
        struct ListNode* slow = head;
        struct ListNode* fast = head;
        //先判断链表是否带环
        while(fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
            if(slow == fast)
            {
                //相遇
                struct ListNode* meetNode = slow;
                while(meetNode != head)//公式证明
                {
                    meetNode = meetNode->next;
                    head = head->next;
                }
                return meetNode;//返回环的入口点
            }
        }
        //链表不带环
        return NULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    在这里插入图片描述

    👉环形链表问题转化成链表相交问题👈

    其实《环形链表II》还有第二种思路,就是将环形链表问题转换成链表相交问题。如果不了解链表相交问题的话,可以看博主的上一篇博客:链表OJ题,里面有对链表相交问题的详细分析。在这里,也给大家实现一下这种思路。
    在这里插入图片描述

    /*
     * struct ListNode {
     *     int val;
     *     struct ListNode *next;
     * };
     */
    struct ListNode *detectCycle(struct ListNode *head) 
    {
        struct ListNode* slow = head;
        struct ListNode* fast = head;
        //先判断链表是否带环
        while(fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
            if(slow == fast)
            {
                //相遇
                struct ListNode* meetNode = slow;
                //链表1的长度从head记到meetNode
                //链表2的长度从meetNode的下一个节点记到meetNode
                struct ListNode* list1 = head;
                struct ListNode* list2 = meetNode->next;
                int len1 = 1;
                int len2 = 1;
                //求长度
                while(list1 != meetNode)
                {
                    len1++;
                    list1 = list1->next;
                }
                while(list2 != meetNode)
                {
                    len2++;
                    list2 = list2->next;
                }
                //假设长链表为链表1
                struct ListNode* longList = head;
                struct ListNode* shortList = meetNode->next;
                //调整长链表
                if(len1 < len2)
                {
                    longList = meetNode->next;
                    shortList = head;
                }
                //长度链表先走长度差步
                int gap = abs(len1 - len2);//gap为绝对值函数
                while(gap--)
                {
                    longList = longList->next;
                }
                //找交点
                while(longList != shortList)//不相同就往后找
                {
                    longList = longList->next;
                    shortList = shortList->next;
                }
                return longList;//返回交点
            }
        }
        //链表不带环
        return NULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    在这里插入图片描述


    👉总结👈

    本篇博客讲解了链表中最经典的问题——环形链表问题,该问题在很多大厂的面试也会经常考。希望大家能够掌握掌握环形链表问题。如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家啦!💖💝❣️

  • 相关阅读:
    [CVPR2022] Debiased Learning from Naturally Imbalanced Pseudo-Labels
    Cookie 与 Session的区别
    每日三题 9.19
    【图像分类】2021-CvT
    软件设计师考试学习1
    JAVA基础小结(项目三)
    log4net日志使用示例
    Vue3中图片上传组件封装-element-plus的el-upload二次封装-案例
    Java · 方法的使用 · 方法重载 · 方法递归
    第四十八章 开发自定义标签 - 在action中使用csr标签
  • 原文地址:https://blog.csdn.net/m0_63639164/article/details/126430849