LeetCode 142:环形链表 II —— 快慢指针定位环的起点(JavaScript)

🌟 题目描述

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

💡 注意:不允许修改链表结构。

示例 1:

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

示例 2:

ini 复制代码
输入: head = [1,2], pos = 0
输出: 返回索引为 0 的节点(值为 1)

示例 3:

ini 复制代码
输入: head = [1], pos = -1
输出: null

🔍 问题分析

这是一道经典的 环检测 + 环入口定位 问题。关键点:

  • 如何判断链表是否有环?
  • 如果有环,如何找到环的起始节点

直接遍历会陷入死循环,所以需要更聪明的方法------快慢指针(Floyd 循环查找算法)


💡 核心思路:Floyd 判圈算法

✅ 第一步:用快慢指针判断是否存在环

  • slow 指针每次走一步;
  • fast 指针每次走两步;
  • 若存在环,两者一定会相遇(因为快指针会追上慢指针);
  • 若无环,fast 会先到达 null

✅ 第二步:找到环的入口节点

当快慢指针相遇后,从头节点和相遇点同时出发,每次走一步,再次相遇的点就是环的入口

🧠 数学证明简述

设:

  • 链表头部到环入口距离为 a
  • 环的长度为 b
  • 相遇点距离入口为 c

则:

  • 慢指针走了:a + c
  • 快指针走了:a + c + k*b(k 是整数)
  • 因为快指针速度是慢指针的 2 倍:2(a + c) = a + c + k*b
  • 推出:a = k*b - c
  • 即:从头节点走 a 步,和从相遇点走 a 步,会在入口处相遇!

✅ JavaScript 实现(完整代码)

ini 复制代码
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val === undefined ? 0 : val)
 *     this.next = (next === undefined ? null : next)
 * }
 */

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

    // 第一步:快慢指针找相遇点
    while (fast && fast.next) {
        slow = slow.next;
        fast = fast.next.next;

        if (slow === fast) {
            // 找到环,进入第二步:找入口
            let x1 = head;  // 从头开始
            let x2 = fast;  // 从相遇点开始

            while (x1 !== x2) {
                x1 = x1.next;
                x2 = x2.next;
            }

            return x1; // 相遇点即为环的入口
        }
    }

    // 无环,返回 null
    return null;
};

🧠 图解演示(以 3→2→0→-4→2 为例)

yaml 复制代码
链表:3 → 2 → 0 → -4 → ↲ 2(环)
      ↑             ↑
      |             |
      -----------------

1. 快慢指针移动:
   slow: 3 → 2 → 0 → -4 → 2
   fast: 3 → 0 → 2 → -4 → 2 → 0 → 2 → ...

2. 在节点 2 相遇(快慢指针相等)

3. 重置 x1 = head(3), x2 = 相遇点(2)
   x1: 3 → 2
   x2: 2 → 0 → -4 → 2 → ...
   在节点 2 再次相遇 → 返回该节点

📊 复杂度分析

项目 复杂度
时间复杂度 O(n) ------ 最多遍历两次链表
空间复杂度 O(1) ------ 只使用常数额外空间

优势:无需哈希表,不修改链表,适用于大链表场景。


🛠️ 面试常见问题

❓ "为什么相遇后,从头和相遇点同时走能相遇于入口?"

回答:这是由数学关系 a = k*b - c 决定的,两个指针走的路径总长相同,最终会在入口处相遇。

❓ "能否用哈希表解决?"

可以,但空间复杂度为 O(n),而快慢指针法是 O(1),更优。

❓ "如果链表很长,快指针会不会越界?"

不会,因为 while(fast && fast.next) 已经做了安全判断。

✅ 总结

其实这题就先设置两个指针慢的一步一步走而快的则两步两步走如果存在环则必定相遇,如果相遇就可以通过这幅图推出a=c+(n−1)(b+c)那就差不多能写出来了

相关推荐
fangdengfu1234 分钟前
ES分析系统各个服务日志占用量
java·前端·elasticsearch
凌云拓界17 分钟前
文件管理:让AI安全操作你的电脑 ——CogitoAgent开发实战(三)
javascript·人工智能·架构·开源·node.js
凌云拓界1 小时前
联网能力:让AI看见更广阔的世界 ——CogitoAgent开发实战(四)
javascript·人工智能·架构·node.js·创业创新
如竟没有火炬1 小时前
最大矩阵——单调栈
数据结构·python·线性代数·算法·leetcode·矩阵
8Qi81 小时前
LeetCode 1143 & 718:最长公共子序列 / 最长重复子数组
算法·leetcode·职场和发展·动态规划
JustHappy2 小时前
古法编程秘籍(六):程序到底是怎么跑起来的?从 IO 到中断,一次讲明白
前端·后端·全栈
绿算技术2 小时前
万卡推理集群存储选型分析:从核心架构到应用视角
大数据·科技·算法·架构
HYCS2 小时前
用pixi.js实现fabric.js(六):从线性代数的角度理解编辑器交互
前端·javascript·canvas