一、问题描述
给定一个单向链表,判断链表中是否存在环。

- 如果链表有环:输出「有环」
- 如果链表无环:输出「无环」
什么是环?链表最后一个节点的 next 不再指向 NULL,而是指向链表前面的某个节点,形成闭环。
二、解题思路:快慢指针法(Floyd 判圈算法)
这是最经典、最常用、空间复杂度 O (1) 的解法。
核心思想
- 定义两个指针:
- 慢指针 slow :每次走 1 步
- 快指针 fast :每次走 2 步
- 从头节点一起出发
- 如果有环 :快指针一定会在环里追上并相遇慢指针
- 如果无环:快指针会先走到 NULL,结束遍历
- 图解如下:

为什么一定会相遇?
可以想象成:在环形跑道上跑步,一个快一个慢,只要跑道是环,快的一定会套圈追上慢的。
三、核心代码
1. 链表节点结构
cpp
typedef struct Node {
int data; // 数据域
struct Node *next; // 指针域
} Node;
2. 判断是否有环(核心函数)
cpp
int isCycle(Node *head) {
Node *fast = head;
Node *slow = head;
while (fast != NULL && fast->next != NULL) {
slow = slow->next; // 慢指针走1步
fast = fast->next->next; // 快指针走2步
if (fast == slow) { // 相遇 = 有环
return 1;
}
}
return 0; // 快指针到末尾 = 无环
}
关键点:
- 循环条件必须判断
fast != NULL && fast->next != NULL,防止越界 - 相遇就直接返回有环,效率很高
四、完整可运行代码(C 语言)
创建链表、构造环、测试、输出结果
cpp
#include <stdio.h>
#include <stdlib.h>
// 1. 链表节点定义
typedef struct Node {
int data;
struct Node *next;
} Node;
// 2. 创建头节点
Node *initList() {
Node *head = (Node*)malloc(sizeof(Node));
head->next = NULL;
return head;
}
// 3. 尾插法添加节点
Node *insertTail(Node *tail, int data) {
Node *newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->next = NULL;
tail->next = newNode;
return newNode;
}
// 4. 获取尾节点
Node *getTail(Node *head) {
Node *p = head;
while (p->next != NULL) {
p = p->next;
}
return p;
}
// 5. 判断是否有环(核心)
int isCycle(Node *head) {
Node *fast = head;
Node *slow = head;
while (fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
if (fast == slow) {
return 1;
}
}
return 0;
}
// 6. 主函数测试
int main() {
Node *head = initList();
Node *tail = getTail(head);
// 插入数据:1 2 3 4 5 6 7 8
tail = insertTail(tail, 1);
tail = insertTail(tail, 2);
tail = insertTail(tail, 3);
Node *node3 = tail; // 保存3号节点
tail = insertTail(tail, 4);
tail = insertTail(tail, 5);
tail = insertTail(tail, 6);
tail = insertTail(tail, 7);
tail = insertTail(tail, 8);
// 构造环:8 -> 3
tail->next = node3;
// 判断并输出
if (isCycle(head)) {
printf("运行结果:该链表【有环】\n");
} else {
printf("运行结果:该链表【无环】\n");
}
return 0;
}
代码中构造了 1→2→3→4→5→6→7→8→3 的环形链表,isCycle 函数检测到快慢指针相遇,因此输出有环。
五、运行结果
编译运行后,控制台输出:
如果你把构造环的代码注释掉:
cpp
// tail->next = node3;
再次运行,结果变为:
六、总结
- 慢指针 1 步,快指针 2 步
- 相遇 = 有环,快指针到 NULL = 无环
- 时间复杂度 O (n),空间复杂度 O (1)