如何判断链表是否有环,确定环的起点
判断链表是否有环
使用快慢指针确定链表是否有环
快指针(fast) 慢指针(slow)
思路:
让快指针和慢指针从链表的起点同时走,快指针每次走两步,慢指针每次走一步,如果链表没有环,则快指针率先走出链表(走到NULL),慢指针也最终走出链表。如果链表有环,则慢指针和快指针最终会在环内相遇。
当去面试时?面试官会有如下提问?
1、为什么slow指针走一步,fast指针走两步,他们一定会在环里相遇,会不会永远追不上?请证明。
2、slow指针走一步,fast指针走3步,走4步,走n步行不行?为什么?请证明。
3、求环的入口点。
下面给图详解:
证明1:为什么slow指针走一步,fast指针走两步,他们一定会在环里相遇,会不会永远追不上?
答:不会 ,假设slow进环的时候,fast跟slow的距离是N,紧接着追击的过程中,fast往前走两步,slow往前走一步,他们每走一次,他们之间的距离就缩小1。
N
N-1
N-2
.
.
2
1
0 距离缩小到0的时候,就是相遇。
证明2: slow指针走一步,fast指针走3步,走4步,走n步行不行?为什么?
答:假设slow进环的时候,fast跟slow的距离是N,追击的过程中,fast往前3步,slow走1步,他们每走一次,他们之间的距离就缩小2
如果N是偶数,那么为一定会相遇
N
N-2
.
.
2
1
0 当N等于0时,快慢指针相遇。
如果N是奇数
N
N-2
N-4
.
.
1
-1
假设环的周长是C,两者相差距离为C-1,如过C-1是奇数,那么就永远追不上。
详解:看上图。
注意:
如果slow进环时,slow跟fast的差距N是奇数,且环的长度C是偶数,那么他们在环里就会一直打圈,永远不会相遇了。
如何找到环的入口
假设环的C长度很小,环外距离L很长,那么slow指针进环时,fast指针已经转了m圈了。
相遇时慢指针走了X步:
慢指针所走的距离为:L+X
快指针所走的距离为:L+mC+X
如果环C的长度很大,slow进环时,fast还没走完一圈。相遇时,快指针也只走了一圈,这时,快指针所走的距离为:L+C+X;
快慢指针相遇时,快指针所走距离为慢指针所走距离的两倍:
即:2(L+X)=L+mC+X
L+X=mC
L=mC-X
即当一个指针从相遇点开始走,一个指针,从起点开始走,当二者相遇时,就是环的入口点。具体详解,看上图。
代码
判定链表是否带环
bool hasCycle(struct ListNode *head)
{
struct ListNode* fast=head;
struct ListNode* slow=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
return true;
}
}
return false;
}
找到链表环的入口
struct ListNode *detectCycle(struct ListNode *head)
{
struct ListNode* fast=head;
struct ListNode* slow=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
//相遇
struct ListNode* meet=fast;
while(meet!=head)
{
meet=meet->next;
head=head->next;
}
return meet;
}
}
return NULL;
}