-
链表这块很经典了,今天把快慢指针三板斧(判环、入口、长度)全部手写一遍
-
LeetCode 部分(对应 LeetCode 141 & 142 结合)
-
相关题目链接:
-
- 环形链表(判环):leetcode.cn/problems/li…
-
- 环形链表 II(找入口):leetcode.cn/problems/li…
-
-
难度:简单 → 中等
-
时间复杂度:O(n),空间 O(1)
-
核心思路(简短总结): 快指针每次走2步,慢指针走1步。如果有环,快慢必相遇(Floyd判圈)。 相遇后:① 慢指针回起点,快指针留在相遇点,两者同时走1步,再相遇即环入口。 ② 从相遇点继续走一圈,计数即环长度。
-
代码(Go 版本,一套完整实现):
-
Go
// ListNode 定义(LeetCode标准)
type ListNode struct {
Val int
Next *ListNode
}
// 1. 判断是否有环(LeetCode 141)
func hasCycle(head *ListNode) bool {
if head == nil || head.Next == nil {
return false
}
slow, fast := head, head
for fast != nil && fast.Next != nil {
slow = slow.Next
fast = fast.Next.Next
if slow == fast {
return true
}
}
return false
}
// 2. 找到环的入口节点(LeetCode 142)
func detectCycle(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return nil
}
slow, fast := head, head
// 第一阶段:找相遇点
for fast != nil && fast.Next != nil {
slow = slow.Next
fast = fast.Next.Next
if slow == fast {
// 第二阶段:slow从头开始,fast留在相遇点,同时走1步
slow = head
for slow != fast {
slow = slow.Next
fast = fast.Next
}
return slow // 相遇点即入口
}
}
return nil
}
// 3. 计算环的长度(扩展题)
func cycleLength(head *ListNode) int {
if head == nil || head.Next == nil {
return 0
}
slow, fast := head, head
for fast != nil && fast.Next != nil {
slow = slow.Next
fast = fast.Next.Next
if slow == fast {
// 从相遇点开始计数
length := 1
fast = fast.Next
for slow != fast {
fast = fast.Next
length++
}
return length
}
}
return 0 // 无环
}
- C++ 版本
C++
ListNode* detectCycle(ListNode* head) {
if (!head || !head->next) return nullptr;
ListNode *slow = head, *fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast) {
slow = head;
while (slow != fast) {
slow = slow->next;
fast = fast->next;
}
return slow;
}
}
return nullptr;
}
-
易错点/优化点/面试追问:
- 快指针要检查 fast && fast->next(防止空指针)。
- 入口证明:相遇时慢指针走了 k 步,快走了 2k 步,多走的 k 步刚好是环的整数倍。
- 为什么入口相遇?从头到入口距离 = 相遇点到入口距离(数学美妙之处)。
- 无环返回 nil / false / 0。
- 扩展:如果要删除环?(找到入口后断开即可,但慎用)。
-
知识点部分(快慢指针通用场景)
- 快慢指针适用场景:① 找环 ② 找中点(快走2慢走1,快到尾慢在中点) ③ 找倒数第k个(快先走k步,然后一起走)。
- 为什么 O(1) 空间?不用哈希表记录访问节点。
- 面试常问:为什么一定相遇?(龟兔赛跑:兔子在环里每圈追龟子1圈,最终追上)。
- 变种:快乐数(LeetCode 202)、环形数组循环等。
-
今日感悟 以前背过“快慢指针找环”,但今天把判环、入口、长度三段代码全部手写 + 调试,才真正理解为什么入口处会相遇(数学证明太优雅了)。公开打卡让我不能只停在“会用”,必须搞懂原理。Day 6 了,坚持的感觉越来越强,离大厂offer又近了!
-
结束语 明天见! 欢迎评论区:① 你是怎么记快慢指针证明的? ② 代码哪里还能更简洁 ③ 你面试被问过链表环吗?怎么答的 #程序员打卡 #LeetCode #快慢指针 #链表环 #Go语言 #C++