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)那就差不多能写出来了

相关推荐
起名时在学Aiifox18 小时前
前端文件下载功能深度解析:从基础实现到企业级方案
前端·vue.js·typescript
千金裘换酒19 小时前
LeetCode反转链表
算法·leetcode·链表
2501_9418779819 小时前
从配置热更新到运行时自适应的互联网工程语法演进与多语言实践随笔分享
开发语言·前端·python
云上凯歌19 小时前
01 ruoyi-vue-pro框架架构剖析
前端·vue.js·架构
byzh_rc20 小时前
[认知计算] 专栏总结
线性代数·算法·matlab·信号处理
华仔啊20 小时前
JavaScript 如何准确判断数据类型?5 种方法深度对比
前端·javascript
qq_4335545420 小时前
C++ manacher(求解回文串问题)
开发语言·c++·算法
歌_顿20 小时前
知识蒸馏学习总结
人工智能·算法
毕设十刻20 小时前
基于Vue的迅读网上书城22f4d(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js
程序员小寒20 小时前
从一道前端面试题,谈 JS 对象存储特点和运算符执行顺序
开发语言·前端·javascript·面试