1. 环形链表问题概述
链表成环检测是数据结构与算法中的经典问题,也是技术面试中的高频考点。LeetCode 142题"环形链表II"要求在141题简单检测的基础上更进一步——不仅需要判断链表是否存在环,还必须准确找出环的起始节点。这个问题看似简单,却蕴含着巧妙的数学原理和精妙的算法设计。
在实际工程中,环形链表检测算法常用于内存管理、资源调度等场景。比如检测线程等待链表中是否存在死锁环,或是验证自定义内存池中的空闲块链表是否出现错误链接。理解这个问题的解法,不仅能帮助开发者应对技术面试,更能提升对链表结构和指针操作的本质理解。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 问题描述与示例分析
给定一个链表的头节点 head,返回链表开始入环的第一个节点。如果链表无环,则返回 null。需要注意几个关键点:
- 不允许修改链表结构(这是常见面试要求)
- 需要O(1)空间复杂度完成(即不能使用哈希表等额外存储)
- 时间复杂度应尽可能优化
示例1:
code复制输入:head = [3,2,0,-4], pos = 1
输出:返回索引为1的节点
解释:链表中有一个环,其尾部连接到第二个节点
示例2:
code复制输入:head = [1,2], pos = 0
输出:返回索引为0的节点
解释:链表中有一个环,其尾部连接到第一个节点
3. 双指针解法原理详解
3.1 快慢指针的相遇机制
Floyd判圈算法是解决这类问题的经典方法。其核心思想是使用两个指针,一个快指针(每次移动两步)和一个慢指针(每次移动一步)。如果存在环,快指针最终会追上慢指针。
数学证明:
设链表非环部分长度为L,环长度为C。当慢指针进入环时,快指针已经在环中移动了L步(因为快指针速度是慢指针两倍)。此时两指针在环中的相对位置差为C - (L mod C)。
由于快指针每次比慢指针多走一步,它们将在C - (L mod C)步后相遇。这个相遇点距离环起点的距离恰好等于链表头到环起点的距离(L mod C)。
3.2 环起点定位的数学推导
当快慢指针相遇后,将其中一个指针移回链表头部,然后两个指针以相同速度前进。它们再次相遇的节点就是环的起点。
证明:
设相遇点距离环起点为x,则有:
慢指针总路程:L + x
快指针总路程:L + x + n*C (n为快指针在环中绕的圈数)
由于快指针速度是慢指针两倍:
2(L + x) = L + x + nC
=> L + x = nC
=> L = n*C - x
这意味着从链表头到环起点的距离L,等于从相遇点继续走nC - x步。而nC - x正好是从相遇点绕环n-1圈再走到环起点的距离。
4. 完整代码实现与逐行解析
python复制class ListNode:
def __init__(self, x):
