LeetCode-142. 环形链表 II

1、题目描述:

给定一个链表的头节点  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 或者链表中的一个有效索引

    2、代码:

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution
    {
    public:
        ListNode* detectCycle(ListNode* head)
        {
            ListNode *fast = head, *slow = head;
            // 先判断是否有环
            do {
                if (!fast || !fast->next)
                    return nullptr;
                fast = fast->next->next;
                slow = slow->next;
            } while (fast != slow);
    
            // fast == slow, 开始寻找环的最初点
            slow = head;
            while (fast != slow) {
                fast = fast->next;
                slow = slow->next;
            }
    
            // 再次出现 fast == slow,代表找到环的起点
            return fast;
        }
    };

    3、解题思路:

    1. 快慢指针检测环的存在

      • 使用两个指针:slowfast
      • slow每次移动一步,fast每次移动两步。
      • 如果链表中存在环,slowfast一定会在环内相遇。
      • 如果链表中不存在环,fastfast->next会先变为nullptr
    2. 寻找环的入口节点

      • slowfast相遇时,说明链表中存在环。
      • 此时,将一个指针重新指向链表头节点 head,另一个指针保持在相遇点。
      • 两个指针每次都只移动一步,当它们再次相遇时,相遇点就是环的入口节点。
    3. 数学证明

      • 假设链表起点到环入口的距离为a,环的长度为 b
      • slowfast第一次相遇时,fast走过的距离是slow的两倍
      • slow走了 s步,则fast走了2s步。
      • 根据环的性质,2s = s + nbnb是环的整数倍),所以s = nb
      • 环的入口节点需要再走 a 步才能到达,因此从头节点出发的指针和从相遇点出发的指针同步移动,最终会在环入口处相遇。 

    4、疑问点:

    (1)为什么 2s = s + nb ?

    当快慢指针在环中相遇时,快指针比慢指针多走的步数是环长度的整数倍 ,因此快指针走的总步数 2s 等于慢指针走的总步数 s 加上若干圈环的长度 nb,即 2s = s + nb

    (2)为什么从头节点和相遇点同步移动会在环入口处相遇 ?

    假设环外的长度是a,环内的部分,假设相遇点距离入口的距离是k,剩下的环长度是c。

    那整个环的长度是:b = k + c。

    当快慢指针相遇时,慢指针走了a + k步,而快指针走了a + k + n*(k + c),其中n是快指针在环中绕的圈数。

    因为快指针的速度是慢指针的两倍,所以总路程也是两倍。

    所以有等式:2(a + k) = a + k + n*(k + c)。

    简化得到

            a + k = n*(k + c)  == > a = (n-1)*(k +c) + c。==>a = (n-1) * b + c

    这说明,当从相遇点出发,走c步就会到达入口点,而c也就是剩下的环的长度部分。

    这时候,如果让一个指针从head开始,另一个从相遇点开始,每次各走一步。那么当它们相遇时,就是环的入口点。

    因为此时,一个走了a步,另一个走了c + (n-1)*b步,即总长度等于a。这样就刚好在入口相遇。

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

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

    余额充值