一、题目内容
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
二、源代码(部分)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct hashTable {
struct ListNode* key;
UT_hash_handle hh;
};
struct hashTable* hashtable;
struct hashTable* find(struct ListNode* ikey) {
struct hashTable* tmp;
HASH_FIND_PTR(hashtable, &ikey, tmp);
return tmp;
}
void insert(struct ListNode* ikey) {
struct hashTable* tmp = malloc(sizeof(struct hashTable));
tmp->key = ikey;
HASH_ADD_PTR(hashtable, key, tmp);
}
bool hasCycle(struct ListNode* head) {
hashtable = NULL;
while (head != NULL) {
if (find(head) != NULL) {
return true;
}
insert(head);
head = head->next;
}
return false;
}
三、题目解析
1. 数据结构定义
struct hashTable:用于实现哈希表的节点结构,包含两个成员:struct ListNode* key:存储链表节点的指针(作为哈希表的键)。UT_hash_handle hh:UT_hash 库的内部句柄,用于维护哈希表的结构(如哈希值、链表指针等)。
struct hashTable* hashtable:全局哈希表指针,作为整个哈希表的入口。
2. 哈希表操作函数
-
find函数:在哈希表中查找指定的链表节点。- 调用
HASH_FIND_PTR宏(UT_hash 库提供),通过链表节点指针ikey在哈希表hashtable中查找对应节点。 - 若找到则返回该哈希表节点,否则返回
NULL。
- 调用
-
insert函数:向哈希表中插入新的链表节点。- 动态分配一个哈希表节点
tmp,将其key成员指向待插入的链表节点ikey。 - 调用
HASH_ADD_PTR宏(UT_hash 库提供),将tmp插入哈希表hashtable,以key作为哈希键。
- 动态分配一个哈希表节点
3. 核心函数 hasCycle
- 功能:判断单链表是否存在环(即链表中是否有节点被重复访问)。
- 逻辑:
- 初始化哈希表为空(
hashtable = NULL)。 - 遍历链表:从
head开始,逐个访问节点。 - 检查当前节点是否已在哈希表中:若
find(head) != NULL,说明该节点之前已被访问过,链表存在环,返回true。 - 若未找到,将当前节点插入哈希表,继续访问下一个节点(
head = head->next)。 - 若遍历结束(
head == NULL)仍未发现重复节点,说明链表无环,返回false。
- 初始化哈希表为空(
四、实验总结
1. 实验目的
验证单链表中是否存在环,通过哈希表记录已访问的节点,实现环的检测。
2. 实验原理
- 利用哈希表的快速查找特性(平均时间复杂度
O(1)),记录链表中每个被访问过的节点。 - 若遍历过程中再次遇到已存在于哈希表中的节点,说明链表存在环(该节点是环的入口或环上的重复节点);若遍历至链表末尾(
head == NULL),则无环。
3. 实验结果分析
- 正确性 :该算法能准确检测链表是否有环。对于有环链表,会在首次重复访问节点时返回
true;对于无环链表,遍历结束后返回false。 - 时间复杂度 :
O(n),其中n是链表节点数。每个节点的插入和查找操作平均为O(1),整体遍历一次链表。 - 空间复杂度 :
O(n),最坏情况下(无环链表),哈希表需存储所有n个节点的指针。
4. 优缺点
- 优点:逻辑直观,实现简单,依赖哈希表的高效查找能力,能在遍历一次链表后得出结果。
- 缺点 :需要额外的哈希表空间存储节点,空间复杂度较高。相比之下,"快慢指针法" 可在
O(1)空间复杂度下实现环检测,更适用于空间敏感的场景。
5. 结论
本代码通过哈希表记录已访问节点的方式,成功实现了单链表的环检测功能,逻辑正确且易于理解,但空间开销较大。在实际应用中,可根据空间限制选择更优的算法(如快慢指针)。