带环链表就是字面意思带环的链表,例如以下这三种情况
练习题
1.给定一个链表,判断链表中是否带环. - 力扣(LeetCode)
思路:快慢指针,慢指针走一步,快指针走两步,两个指针从链表的起始位置开始走,如果链表带环,快慢指针一定会在环中相遇,否则快指针先走到链表的末尾。
当slow指针进环时,fast和slow追及(一定能追上),证明过程如下
证明:假设slow进环时,与fast指针相距N,快慢指针的速度差为1,所以快慢指针之间的距离变化为
N->N-1->N-2->N-3->........->2->1->0,每追击一次,距离减小1,当减小为0时,就是追上了,这里最差的情况就是两个指针之间的距离刚好就是环的长度。
所以,在慢指针走一圈之前,快指针肯定是可以追上慢指针的
问题扩展:
1.快指针走一次走3步,4步,n步可以吗?
当快指针走3步时,两指针之间的距离变化为
偶数 奇数
N N
N-2 N-2
N-4 N-4
... ...
4 3
2 1
0(追上了) -1(错过了进行新一轮的追击),快慢指针之间的距离变为C-1(C为环的长度),这里又有两种情况,C-1分为奇数和偶数
总结:
1.N是偶数,第一轮就追上了
2.N是奇数,第一轮就会错过,距离变为C-1
a.如果c-1是偶数,下一轮就追上
b.如果c-1是奇数,就永远也追不上了
所以,如果同时存在N是奇数和C是偶数,就永远追不上(实际上这种情况并不存在)
证明:假设slow进环时,fast和slow之间的距离为N,此时fast已在环里走了x圈,slow走的距离为L,fast走L+x*C+C-N,fast走的距离是slow的3倍
3*L=L+x*C+C-N
2*L=(x+1)*C-N,如果同时存在N是奇数和C是偶数,这个等式就不成立,C和N只能同时为奇数或者偶数
结论:一定能追上,N为偶数第一轮就追上,N为奇数第一轮追不上,但C-1为偶数第二轮就可以追上。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) {
ListNode*slow=head;
ListNode*fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(fast==slow)
return true;
}
return false;
}
2.给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。. - 力扣(LeetCode)
结论:相遇后,slow指针指针继续走,cur指针起始位置开始走,相遇的位置就是入环的第一个节点
证明:slow指针走过的距离L+N,fast走的为L+N+C*x,fast走过的距离是slow的2倍
2*(L+N)=L+N+C*x
L=C*x-N=(x-1)*C+(C-N)x>=1
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head) {
ListNode*slow=head;
ListNode*fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
{
ListNode*meet=slow;
while(meet!=head)
{
head=head->next;
meet=meet->next;
}
return meet;
}
}
return NULL;
}