一、先搞懂:什么是链表有环?
正常链表是一条直线:1 → 2 → 3 → 4 → 5 → NULL
有环的链表,尾巴会指回前面某个节点,形成一个圈:1 → 2 → 3 → 4 → 5 → 6 → 回到 3这样永远走不到头,就是带环链表 。我们的目标:找到这个环的入口节点 (例子里是 3)。
二、核心思路:
整个算法分为3 个关键步骤,逻辑非常清晰:
- 判断链表有没有环
- 算出环里有多少个节点
- 用 "先走环长步" 的方法找到入口
三、第一步:先快慢指针判断有环
这是最经典的快慢指针法:
-
快指针 fast:每次走 2 步
-
慢指针 slow:每次走 1 步
-
如果链表无环:fast 会走到 NULL
-
如果链表有环 :fast 一定会在环里追上 slow,两者相遇
原理很简单:快指针速度是慢指针的 2 倍,进环后迟早会 "套圈" 追上。
四、第二步:计算环的节点个数
当快慢指针相遇后:
- 固定 slow 不动
- 让 fast 继续一步步走,每走一步计数
- 当 fast 再次回到 slow 时,统计的总数就是环的长度
在我们的例子里,环长 = 6 个节点 。
五、第三步:先走环长步,再同步走 (关键点)
这是找到入口的核心思想:
- 把 fast 和 slow 重新回到链表头节点
- 让 fast 先走 "环长" 步(我们这里是先走 6 步)
- 然后 fast 和 slow 以相同速度,每次都走 1 步
- 它们相遇的地方,就是环的入口!
为什么这样能找到入口?
- 快指针先走环长步,相当于提前绕环一圈
- 之后两者同步前进,距离刚好完美匹配
- 当慢指针走到环入口时,快指针也刚好走到这里
六、核心代码
cpp
// 找到环的入口节点
Node* findBegin(Node *head)
{
Node *fast = head;
Node *slow = head;
// 1. 快慢指针相遇,判断有环
while(fast != NULL && fast->next != NULL)
{
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
{
// 2. 计算环的节点数
int count = 1;
while(fast->next != slow)
{
count++;
fast = fast->next;
}
// 3. 重新指向头节点
fast = head;
slow = head;
// fast 先走 count 步
for (int i = 0; i < count; i++)
{
fast = fast->next;
}
// 同步走,相遇就是入口
while(fast != slow)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
}
return NULL;
}
七、完整可运行完整代码
cpp
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node *next;
} Node;
Node* initList() {
Node *head = (Node*)malloc(sizeof(Node));
head->next = NULL;
return head;
}
Node* insertTail(Node *tail, int data) {
Node *newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->next = NULL;
tail->next = newNode;
return newNode;
}
// 核心函数
Node* findBegin(Node *head)
{
Node *fast = head;
Node *slow = head;
while(fast != NULL && fast->next != NULL)
{
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
{
int count = 1;
while(fast->next != slow)
{
count++;
fast = fast->next;
}
fast = head;
slow = head;
// fast 先走环长步
for (int i = 0; i < count; i++)
{
fast = fast->next;
}
while(fast != slow)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
}
return NULL;
}
int main()
{
Node *list = initList();
Node *tail = list;
tail = insertTail(tail, 1);
tail = insertTail(tail, 2);
tail = insertTail(tail, 3);
Node *enter = tail; // 环入口
tail = insertTail(tail, 4);
tail = insertTail(tail, 5);
tail = insertTail(tail, 6);
tail = insertTail(tail, 7);
tail = insertTail(tail, 8);
// 构造环:8 → 3
tail->next = enter;
Node *res = findBegin(list);
printf("环的入口节点是:%d\n", res->data);
return 0;
}
八、运行结果

九、算法总结
这套方法的核心思想可以浓缩成三句话:
- 快慢指针相遇 = 链表有环
- 相遇点绕一圈 = 算出环长
- 快指针先走环长步,再同步走 = 找到环入口
时间复杂度 O (n),空间复杂度 O (1),是最优解法。