一.什么是环形链表
环形链表是一种链表的尾结点指向头结点或者其他结点的特殊结构。这个就是环形链表的一种:
二.环形链表的判定
2.1具体题目的举例
2.2解题思路
通过快慢指针来判定,一个指针为慢指针一次走一个,一个指针为快指针一次走两个,如果该链表为环形链表,快指针先进入环形,慢指针后进入,该题变为追击问题,则两指针一定会相遇。慢指针刚进入时,两指针的距离,最好的情况是刚好相遇;最坏的情况为相距一个环。我们只用判断两指针是否会相遇,如果链表无环,fast会先走到空。
2.3思路的解释
slow一次走一个,fast指针一次走两个为什么能追上?fast一次走3,4,5~n呢?
为什么会相遇有没有可能一直错过?
2.3.1slow一次走一个,fast指针一次走两个为什么能追上?
假设slow刚进入环时两指针间的距离为N,fast一次走两位,slow一次走一位,则两者的的相对运动距离为1。则fast到slow的距离变化如下:
N--N-1---N---2----N-3-----------0 每追击一次距离减少一个,为0时追上了。
2.3.4fast一次走3,4,5~n呢?
假设slow刚进入环时两指针间的距离为N,fast一次走三位,slow一次走一位,则两者的的相对运动距离为2。则fast到slow的距离变化分为两类(N为奇数和偶数的情况)如下:
1.N为奇数时:N→N-2→N-4→……1→错过了进行新一轮的追击:设这个环形的长度为C,这时两指针的距离为C-1,这时C要分为奇数偶数,分类讨论:
①C为奇数,则C-1为偶数:
C-1→C-3→C-5→……0追到了
②C为偶数,则C-1为奇数:
C-1→C-3→C-5→……1→错过了进行新一轮的追击,同样是该情况。
2.N为偶数时:N→N-2→N-4→……0 追到了
总结:
1.N为偶数时,第一轮就追到了
2.N为奇数时,第一轮追击会错过,两者距离变为C-1
a.C-1为偶数时,第二轮就追击上了。
b.C-1为奇数时,永远追不上。
由上面推理可知fast一次走3,4,5~n情况类似。
同时存在N是奇数C为偶数时永远追不上。
2.3.5 判断相遇是否会一直错过?
假设slow刚进入环时两指针间的距离为N,fast一次走三位,slow一次走一位,则两者的的相对运动距离为2。设从开始到入环的距离为L,当slow刚如环时,slow运动了L,fast运动了
L+X*C+C-N X为fast绕了几圈环。
且fast的移动位移为slow的3倍,可得:
fast=3*slow
L+X*C+C-N=3L
2L=(X+1)*C-N 由这个式子通过数学分析得:2L是偶数
偶数=(X+1)*C-N 如果要让(X+1)*C-N为偶数则:
①N为偶数时,C为偶数
②N为奇数时,C为奇数
由上个分析可知N是奇数C为偶数时永远追不上
我们已经分析了两种情况,N是奇数C为偶数的情况不可能出现,所以一定会相遇。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool hasCycle(struct ListNode *head) {
struct ListNode *slow=head;
struct ListNode *fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(fast==slow)
{
return true;
}
}
return false;
}
三.寻找环的入口
3.1具体题目的举例
3.2解题思路
将头节点记为head,head指针一次移动一位,另fast指针一次走2位,slow指针一次走一位,找到fast和slow指针相遇的地点记为meet,meet指针一次移动一位,meet和head同时移动相遇时即为环的入口。
3.3思路的解释
假设slow刚进入环时两指针间的距离为N,fast一次走二位,slow一次走一位,则两者的的相对运动距离为1。设环的长度为C,设从开始到入环的距离为L,设slow与fast相遇时在环中移动了N,当两者相遇时
slow移动了:L+N
fast移动了:L+X*C+N X为fast绕了几圈环。
fast=2*slow
L+X*C+N =2*(L+N)
L=(X-1)*C+C-N(X>=1)
由此公式可知C-N=L,C-N又是meet到环入口的距离,所以meet和head的相遇点为环的入口。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode *slow=head;
struct ListNode *fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
{
struct ListNode *meet=slow;
while(meet!=head)
{
meet=meet->next;
head=head->next;
}
return meet;
}
}
return NULL;
}