0142.环形链表 II

难度:🟡 中等

标签哈希表链表双指针

链接142. 环形链表 II

题目描述

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

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

不允许修改 链表。

示例 1:

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

示例 2:

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

示例 3:

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

解题思路

核心思想

本题是“环形链表”问题的进阶,不仅要判断是否存在环,还要找到环的入口节点。解决此问题的最优方法是 弗洛伊德的龟兔赛跑算法(Floyd's Cycle-Finding Algorithm),它分为两个阶段:

  1. 相遇:使用快慢指针,快指针每次走两步,慢指针每次走一步。如果它们能够相遇,则证明链表中存在环。

  2. 寻找入口:当快慢指针相遇后,将其中一个指针(例如,一个新的指针 ptr)放回链表头部 head,然后让 ptr 和相遇点的慢指针 slow 同时以每次一步的速度前进。它们再次相遇的节点,就是环的入口。

思路选择

快慢指针法 是解决此问题的标准最优思路,因为它不需要额外的存储空间,空间复杂度为 O(1)。

  • 第一阶段(判断环并相遇):与判断环形链表(141题)的逻辑完全相同。

  • 第二阶段(寻找入口):这一步的正确性基于一个数学推导。设链表头到环入口的距离为 a,环入口到相遇点的距离为 b,相遇点再到环入口的距离为 c。当快慢指针相遇时,慢指针走了 a+b 的距离,快指针走了 a+b+k(b+c)(其中 k 是快指针在环内多跑的圈数)。因为快指针速度是慢指针的两倍,所以 2(a+b) = a+b+k(b+c),化简得 a = k(b+c) - b = (k-1)(b+c) + c。这个公式的物理意义是:从头节点到环入口的距离 a,等于从相遇点到环入口的距离 c 再加上 k-1 圈环的长度。因此,一个指针从 head 出发,另一个指针从相遇点出发,都以相同速度前进,它们必然会在环的入口相遇。

关键步骤

  1. 初始化:创建快慢指针 fastslow,都指向头节点 head

  2. 第一阶段:寻找相遇点: a. 在循环中,fast 每次移动两步,slow 每次移动一步。 b. 如果 fast 到达链表末尾 (null),说明无环,返回 null。 c. 如果 fast === slow,说明相遇,跳出循环。

  3. 第二阶段:寻找入口点: a. 创建一个新的指针 finder,指向头节点 head。 b. 同时移动 finderslow 指针,每次都移动一步。 c. 当 finder === slow 时,它们相遇的节点就是环的入口,返回该节点。

代码实现

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 * this.val = val;
 * this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var detectCycle = function (head) {
    let slow = head, fast = head;

    while (fast !== null && fast.next !== null) {
        fast = fast.next.next;
        slow = slow.next;

        // 快慢指针相遇
        if (fast === slow) {
            let finder = head;
            // 从头节点和相遇点同时出发
            while (finder !== slow) {
                finder = finder.next;
                slow = slow.next;
            }
            // 再次相遇点即为环的入口
            return finder;
        }
    }

    // 没有相遇,说明无环
    return null;
};

复杂度分析

  • 时间复杂度O(n) 其中 n 是链表的长度。两个阶段的遍历都与链表长度成线性关系。

  • 空间复杂度O(1) 我们只使用了常数个额外变量作为指针,空间消耗是恒定的。

相关题目

总结

本题是快慢指针技巧的深度应用,完美地展示了该算法不仅能判断环的存在,还能精确定位环的入口。理解其背后的数学原理是掌握此算法的关键,也是面试中常被追问的要点。

Copyright © Jun 2025 all right reserved,powered by Gitbook该文件修订时间: 2025-07-03 17:35:08

results matching ""

    No results matching ""

    results matching ""

      No results matching ""