LeetCode-链表-环形链表+环形链表II
✏️ 关于专栏:专栏用于记录
prepare for the coding test
。
📝 环形链表
🎯题目描述
给你一个链表的头节点
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
解释:链表中没有环。
🧩题目提示
- 链表中节点的数目范围是
[0, 104]
-105 <= Node.val <= 105
pos
为-1
或者链表中的一个 有效索引 。
🧪AC
使用快慢指针思想,相遇即证明链表有环。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast&&fast->next){
fast = fast->next->next;
slow = slow->next;
if(fast == slow){
return true;
}
}
return false;
}
};
📝 环形链表||
🎯题目描述
给定一个链表的头节点
head
,返回链表开始入环的第一个节点。 如果链表无环,则返回null
。如果链表中有某个节点,可以通过连续跟踪
next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果pos
是-1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。
🔗题目链接:环形链表||
🔍 输入输出示例
示例 1:

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

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

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
🧩题目提示
- 链表中节点的数目范围在范围
[0, 104]
内 -105 <= Node.val <= 105
pos
的值为-1
或者链表中的一个有效索引
🧪AC

假设从链表头节点到环的入口节点,需要走 a
步;环的长度为 c
。
当快慢指针首次相遇时,设慢指针一共走了 b
步,由于快指针的速度是慢指针的两倍,因此快指针此时走了 2b
步。
又因为快指针比慢指针多走了若干圈(设为 k
圈),即比慢指针多走了 k * c
步,便有:
2b - b = k * c → b = k * c
此时慢指针已经在环中走了 b
步,即绕了 k
圈,当前位置是相遇点。
从相遇点开始,慢指针再继续走 a
步,刚好可以回到环的入口节点(因为相遇点与入口之间刚好差 kc - a
步,走 a
步正好抵消这个差值)。
所以如果此时从头节点再派出一个指针,和慢指针一起每次走一步,那么两个指针最终一定会在环的入口节点相遇。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast&&fast->next){
fast = fast->next->next;
slow = slow->next;
if(fast == slow){
while(slow != head){
slow = slow->next;
head = head->next;
}
return slow;
}
}
return nullptr;
}
};
🌟 总结
双指针(快慢指针)思想是关键:
- 快指针每次走两步,慢指针每次走一步。
- 如果存在环,快慢指针最终一定会在环中相遇;如果无环,快指针会先走到链表尾部(即
nullptr
)。
检测是否有环(环形链表 I):
- 相遇即表示链表中有环,时间复杂度 O(n),空间复杂度 O(1)。
找到入环节点(环形链表 II):
- 第一次相遇后,将其中一个指针移动到头节点,然后两个指针同时每次走一步,再次相遇的位置就是入环点。
指针每次走一步。 - 如果存在环,快慢指针最终一定会在环中相遇;如果无环,快指针会先走到链表尾部(即
nullptr
)。
检测是否有环(环形链表 I):
- 相遇即表示链表中有环,时间复杂度 O(n),空间复杂度 O(1)。
找到入环节点(环形链表 II):
- 第一次相遇后,将其中一个指针移动到头节点,然后两个指针同时每次走一步,再次相遇的位置就是入环点。
- 数学原理保证这个过程是正确的,具体可参考“相遇点推理法”。