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

相关推荐
无极低码31 分钟前
ecGlypher新手安装分步指南(标准化流程)
人工智能·算法·自然语言处理·大模型·rag
漫随流水32 分钟前
旅游推荐系统(view.py)
前端·数据库·python·旅游
软件算法开发1 小时前
基于海象优化算法的LSTM网络模型(WOA-LSTM)的一维时间序列预测matlab仿真
算法·matlab·lstm·一维时间序列预测·woa-lstm·海象优化
superior tigre2 小时前
22 括号生成
算法·深度优先
踩着两条虫2 小时前
VTJ.PRO 核心架构全公开!从设计稿到代码,揭秘AI智能体如何“听懂人话”
前端·vue.js·ai编程
努力也学不会java3 小时前
【缓存算法】一篇文章带你彻底搞懂面试高频题LRU/LFU
java·数据结构·人工智能·算法·缓存·面试
jzlhll1233 小时前
kotlin Flow first() last()总结
开发语言·前端·kotlin
旖-旎3 小时前
二分查找(x的平方根)(4)
c++·算法·二分查找·力扣·双指针
用头发抵命3 小时前
Vue 3 中优雅地集成 Video.js 播放器:从组件封装到功能定制
开发语言·javascript·ecmascript
ECT-OS-JiuHuaShan3 小时前
朱梁万有递归元定理,重构《易经》
算法·重构