C++判断链表是否有环并找到入口结点的算法详解

程一诺
程一诺
发布时间:2026-10-16| 6人浏览过
原创

在C++面试及算法练习中,如何高效地找出链表的环入口结点是一个经典考点。本文详细解析了满足O(1)空间复杂度的快慢指针算法及其数学原理,同时提供基于哈希表的辅助解法。

问题描述与约束

给定一个长度为 $n$ 的链表,若其中包含环,请找出该环的入口结点;若不存在环,则返回 null。

  • 数据范围:$n \le 10000$,结点值介于 1 到 10000 之间。
  • 核心要求:时间复杂度需为 O(n),空间复杂度必须控制在 O(1)(即不能使用额外的哈希集合或数组)。
  • 输入说明:链表分为入环前部分和环内部分,例如输入 {1,2} 和 {3,4,5} 时,实际构成的环形结构如下图所示:

    如上图中,入口结点值为 3,程序应返回指向该结点的指针,后台将打印对应的结点值。

解题思路

针对此题,主要有两种常见的解决路径。第一种是利用哈希表记录访问过的节点,但受限于空间复杂度 O(1) 的要求,通常不作为最优解,这里仅作对比说明。第二种是经典的「快慢双指针法」,这是本题的标准答案。

解法一:哈希 Set(空间复杂度 O(n))

虽然不符合最严格的 O(1) 空间要求,但该逻辑最直观。使用 std::unordered_set 存储遍历过的指针地址。每当遇到新节点时检查是否已存在于集合中:

  1. 若不存在,将其插入集合并继续向下遍历。
  2. 若发现该指针已在集合中,说明当前节点即为环的入口结点。

解法二:快慢双指针(推荐,空间复杂度 O(1))

这是本题的核心解法,其正确性基于以下数学推导过程:

  1. 定义变量:设头节点 A 到环入口 B 的距离为 $X$,环入口 B 到第一次相遇点 C 的距离为 $Y$,环 C 到 B 的剩余距离为 $Z$。环周长即为 $Y+Z$。
  2. 第一次相遇:当快慢指针在 C 点相遇时,慢指针走了 $X+Y$。因为快指针速度是慢指针的 2 倍,所以快指针走了 $2(X+Y)$。此时快指针不仅在链表中走了一次 X+Y,还在环里多走了若干圈。
  3. 距离关系:快指针比慢指针多走的距离一定是环周长的整数倍,即:$2(X+Y) - (X+Y) = Y+Z = k(Y+Z)$。推导出 $X = (k-1)(Y+Z) + Z$。这意味着,从起点 A 走 X 步到达入口 B 的距离,等同于从相遇点 C 走 Z 步再绕环一圈的距离。
  4. 确定入口:既然 $A \rightarrow B$ 的距离等于 $C \rightarrow B$ 加上环长,我们可以将其中一个指针重置回头节点 A,让两个指针以相同的速度(每次一步)前进。当它们再次相遇时,正好就是环入口 B。

C++ 实现代码

1. 哈希 Set 实现

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead) {
        std::unordered_set<ListNode*> S;
        while(pHead != nullptr)
        {
            // 如果节点已经存在,说明找到了环入口
            if(S.find(pHead) != S.end()) 
            {
                return pHead;
            }
            // 否则存入集合并移动指针
            S.insert(pHead);
            pHead = pHead->next;
        }
        return nullptr;
    }
};

2. 快慢双指针实现(O(1) 空间)

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead) {
        // 边界情况:空链表无环
        if(pHead == nullptr || pHead->next == nullptr) 
            return nullptr;
        
        // 初始化快慢指针
        ListNode* slow = pHead;
        ListNode* fast = pHead;
        
        // 第一阶段:寻找相遇点
        while(fast != nullptr && fast->next != nullptr)
        {
            slow = slow->next;           // 慢指针走 1 步
            fast = fast->next->next;     // 快指针走 2 步
            
            // 如果相遇,说明有环
            if(slow == fast) 
                break;
        }
        
        // 若 fast 为空,说明未形成闭环
        if(fast == nullptr || fast->next == nullptr)
            return nullptr;
        
        // 第二阶段:寻找入口点
        // 将快指针重置到头部,两者同速前进
        fast = pHead;
        while(fast != slow)
        {
            fast = fast->next;
            slow = slow->next;
        }
        
        // 相遇点即为环入口
        return fast;
    }
};

总结

查找链表环入口的关键在于利用快慢指针的速度差构建数学等式。一旦确认有环,只需将一个指针放回起点同步移动即可精准定位。以上就是如何通过C++求出链表中环的入口结点的详细内容,更多关于C++链表环检测的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。