【Hot 100 刷题计划】 LeetCode 142. 环形链表 II | C++ 哈希表直觉解法

LeetCode 142. 环形链表 II

📌 题目描述

题目级别:中等

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

不允许修改 链表。

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

💡 解法一:哈希表备忘录

想要找到"环的入口",最直接的想法就是"留下脚印"。

我们在遍历链表的过程中,把路过的每一个节点的指针都记在哈希表(HashSet)里。

不断往前走,每走到一个新节点,都先去查一下表:

  1. 如果表中不存在,就把当前节点记录下来,继续往下走。
  2. 如果走到某个节点,发现它已经在哈希表里了 !那么毫无疑问,这个节点就是我们绕了一圈后第一次重新踏足的地方,它就是环的入口节点
  3. 如果一路走到 nullptr,说明这是一条直线,直接返回 null

💻 C++ 代码实现 (哈希表法)

cpp 复制代码
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        // 使用 unordered_set 记录已经访问过的节点
        unordered_set<ListNode*> seen;
        
        while (head != nullptr)
        {
            // 如果当前节点已经被访问过,它就是环的入口
            if (seen.count(head)) return head;
            
            // 记录当前节点
            seen.insert(head);
            // 向后移动
            head = head->next;
        }

        // 走到了链表尽头,无环
        return nullptr;
    }
};

💡 解法二:Floyd 判环算法 (龟兔赛跑进阶)

这道题是考察数学推导的经典之作。我们可以用快慢指针分两阶段解决:

阶段 1:判断是否有环,并找到相遇点

定义快指针 fast (每次 2 步) 和慢指针 slow (每次 1 步)。如果它们在某处相遇了,说明一定有环。

阶段 2:寻找环的入口 (数学推导)

设起点到入口的距离为 xxx。

设入口到相遇点的距离为 yyy。

设相遇点到入口的剩余距离为 zzz。

  • slow 走了 x+yx + yx+y
  • fast 走了 x+y+n(y+z)x + y + n(y + z)x+y+n(y+z)
    因为 fast 的速度是 slow 的两倍:
    2(x+y)=x+y+n(y+z)2(x + y) = x + y + n(y + z)2(x+y)=x+y+n(y+z)
    化简得到:
    x=(n−1)(y+z)+zx = (n - 1)(y + z) + zx=(n−1)(y+z)+z
    这个极其漂亮的公式告诉我们:起点到环入口的距离 xxx,刚好等于相遇点走到环入口的距离 zzz(再加上几圈闲逛)。

行动指南:

当两指针相遇后,我们把其中一个指针扔回起点 ,另一个留在相遇点。然后两人速度保持一致(都每次走 1 步)。当他们再次相遇时,相交的节点必定是环的入口!


💻 C++ 代码实现 (快慢指针最优解)

cpp 复制代码
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if (!head || !head->next) return nullptr;

        ListNode *slow = head;
        ListNode *fast = head;

        // 阶段 1:快慢指针寻找相遇点
        while (fast && fast->next) {
            slow = slow->next;
            fast = fast->next->next;

            // 如果相遇了,进入阶段 2
            if (slow == fast) {
                // 将其中一个指针重置到头部
                ListNode *p1 = head;
                ListNode *p2 = slow;

                // 两人以相同速度前进,相遇点即为环入口
                while (p1 != p2) {
                    p1 = p1->next;
                    p2 = p2->next;
                }
                
                return p1;
            }
        }

        return nullptr;
    }
};
相关推荐
rannn_1117 小时前
【力扣hot100】链表专题|160、206、234、141、142
java·算法·leetcode·链表·面试·开发
不会代码的小猴8 小时前
21. 泛型编程上
开发语言·c++·笔记·算法
营养充电站8 小时前
VS Code Git 工作树:解锁多分支并行开发的高效体验
leetcode·决策树·逻辑回归·散列表·模拟退火算法
青瓦梦滋8 小时前
传输层UDP/TCP协议
linux·网络·c++·网络协议·tcp/ip·udp
一只旭宝9 小时前
细讲C加加【9】C++ std::function与std::bind详解|仿函数、绑定器、类成员绑定、占位符、成员偏移指针
开发语言·c++·算法
良木林9 小时前
子串 - LeetCode hot 100
算法·leetcode·职场和发展
蒸蒸yyyyzwd10 小时前
秋招不熟悉力扣学习笔记1
笔记·学习·leetcode
Lhan.zzZ11 小时前
在 Visual Studio 2022 中打造可扩展的动态链接库模块:从零搭建到原理解析
开发语言·c++·visual studio
LuminousCPP11 小时前
数据结构-双向循环链表
c语言·数据结构·笔记·链表
zh路西法13 小时前
【3D SLAM源码解读系列】(二)Small_gicp——5 个积木搭出最优点云配准
c++·pcl·icp·fastgicp·smallgicp·gicp