<数据结构>刷题笔记——链表篇进阶一(图文详解)

149 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

1. 环形链表

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

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

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

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

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

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

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

【思路】

快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表其实位置开始运行,如果链表带环则一定会在环中相遇,否则快指针率先走到链表的末尾。

为什么快指针每次走两步,慢指针走一步可以? 假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚进环时,可能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。 此时,两个指针每移动一次,之间的距离就缩小一步,不会出现每次刚好是套圈的情况,因此:在满指针走到一圈之前,快指针肯定是可以追上慢指针的,即相遇。(这里的套圈指的是越过,错过) 在这里插入图片描述

【扩展问题】

快指针一次走3步,走4步,...n步行吗? 在这里插入图片描述 慢指针一次走1步,快指针一次走4步,也就是它们的速度差为3的情况更加复杂了,可能第一圈的时候被套圈,第n圈的时候刚好追上呢。其它情况感兴趣的自己研究研究,我在这里就不细说了。

【参考代码】

bool hasCycle(struct ListNode *head) {
    struct ListNode* fast,*slow;
    fast = slow = head;
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
            return true;
    }
    return false;
    
}

【链接】

力扣141. 环形链表

2. 环形链表 II

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

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

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

示例 2: 在这里插入图片描述 输入:head = [1,2], pos = 0 输出:返回索引为 0 的链表节点 解释:链表中有一个环,其尾部连接到第一个节点。

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

提示: 链表中节点的数目范围在范围 [0, 104] 内 -105 <= Node.val <= 105 pos 的值为 -1 或者链表中的一个有效索引

【思路】

这道题跟上一道题的不同点在于它要求找到入环的第一个节点。想达成这个目的,我们得好好分析分析。 让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇。 在这里插入图片描述 这是一道数学证明题,很巧妙也很难想到

【思路二】

其实还有另一种思路:把环剪开,它就会变成力扣160. 相交链表这道题,这题我上一篇讲解过,感兴趣可以去看看。 代码的思路是第一个巧妙的方法。

【参考代码】

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* fast,*slow, *meet, *start;
    start = fast = slow = head;

    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        {
            meet = fast;
            while(start != meet)
            {
                start = start->next;
                meet = meet->next;
            }
            return start;
        }
    }
    return NULL;

}

【链接】

力扣142. 环形链表 II