环形链表2

环形链表2

题目描述

CategoryDifficultyLikesDislikes
algorithmsMedium (58.09%)2590-
  • Tags

    Linked List, Tow Pointers

  • Companies

    Unknown

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

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

不允许修改 链表。

示例 1:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

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

示例 2:

https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2018/12/07/circularlinkedlist_test2.png

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2018/12/07/circularlinkedlist_test3.png

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:

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

**进阶:**你是否可以使用 O(1) 空间解决此题?

方法一:哈希表

class Solution
{
public:
    ListNode *detectCycle(ListNode *head)
    {
        if (!head || !head->next)
            return nullptr;
        unordered_set<ListNode *> set;
        while (set.find(head) == set.end())
        {
            set.insert(head);
            if (head)
                head = head->next;
            else
                return nullptr;
        }
        return head;
    }
};

方法二:快慢指针

我来简洁清晰地分析一下快慢指针吧。

重画链表如下所示,线上有若干个节点。记蓝色慢指针为 slow,红色快指针为 fast。初始时 slow 和 fast 均在头节点处。

https://pic.leetcode.cn/1715514553-RxQrzr-0208_2.png

使 slow 和 fast 同时前进,fast 的速度是 slow 的两倍。当 slow 抵达环的入口处时,fast 一定在环上,如下所示。

https://pic.leetcode.cn/1715514558-mCJsmw-0208_3.png

其中:

  • head 和 A 的距离为 z
  • 弧 AB (沿箭头方向) 的长度为 x
  • 同理,弧 BA 的长度为 y

可得:

  • slow 走过的步数为 z
  • 设 fast 已经走过了 k 个环,k≥0,对应的步数为 z+k(x+y)+x

以上两个步数中,后者为前者的两倍,即 2z=z+k(x+y)+x 化简可得 z=x+k(x+y),替换如下所示。

https://pic.leetcode.cn/1715514562-KmTrNr-0208_4.png

此时因为 fast 比 slow 快 1 个单位的速度,且 y 为整数,所以再经过 y 个单位的时间即可追上 slow 即 slow 再走 y 步,fast 再走 2y 步。设相遇在 C 点,位置如下所示,可得弧 AC 长度为 y

https://pic.leetcode.cn/1715514566-cEsEBC-0208_5.png

因为此前x+y为环长,所以弧 CA 的长度为x。 此时我们另用一橙色指针 ptr (pointer) 指向 head,如下所示。并使 ptr 和 slow 保持 1 个单位的速度前进,在经过 z = x + k ( x + y ) z=x+k(x+y) z=x+k(x+y) 步后,可在 A 处相遇。

https://pic.leetcode.cn/1715514569-ATwmZT-0208_6.png

再考虑链表无环的情况,fast 在追到 slow 之前就会指向空节点,退出循环即可。

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *slow = head, *fast = head;
        while (fast != nullptr) {
            slow = slow->next;
            if (fast->next == nullptr) {
                return nullptr;
            }
            fast = fast->next->next;
            if (fast == slow) {
                ListNode *ptr = head;
                while (ptr != slow) {
                    ptr = ptr->next;
                    slow = slow->next;
                }
                return ptr;
            }
        }
        return nullptr;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泡椒香菜‍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值