目录
- 一、判断链表中是否存在环
- 思路
- 分析
- 二、求环的长度
- 三、求入环点
一、判断链表中是否存在环
题目链接:
判断链表中是否存在环
思路
把它转换为追击问题(快慢指针)
快指针比慢指针多走一步,当快指针正好进入环时,慢指针刚好走到一半;
当慢指针正好入环时,快指针在环里的位置是不确定的,可能已经绕环好几圈了,也可能一圈也没绕完,这个取决于环的长度。
快慢指针都进环后,剩下的问题就是追击问题了,当快指针追上慢指针,说明链表有环。
代码思路:
1.快指针比慢指针多走一步;
2.情况一:如果fast走到空,说明链表是没环的; 情况二:进环-》追击-》追上-》证明链表有环;
bool hasCycle(struct ListNode *head)
{
struct ListNode *slow=head;
struct ListNode *fast=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
return true;
}
}
return false;
}
分析
问题1:为什么slow走一步,fast走两步,他们会相遇?会不会错过?请证明:
假设slow进环时,fast和slow的距离为N;
slow每走1步,fast走2步
他们之间的距离每次缩小1:即N,N-1,N-2…3,2,1,0,当距离为0时就追上了;
问题2:如果slow走1步,fast走3步,他们会不会相遇?请证明:
假设slow进环时,fast和slow的距离为N;
slow每走1步,fast走3步,他们之间的距离每次缩小2:
情况一:(当N为偶数)N,N-2,N-4…4,2,0—能追上
情况二:(当N为奇数)N,N-2,N-4…5,3,1,-1—错过,设环的周长为C,此时slow和fast的距离变为C-1,
进入新的一轮追击:
情况一:(当C为偶数,则C-1为奇数)C-1,C-3,C-5…5,3,1,-1—错过,此时fast和slow不可能再相遇了
情况二:(当C为奇数,则C-1为偶数)C-1,C-3,C-5…4,2,0—能追上
总结:
如果slow走1步,fast走X(X>2)步,他们会不会相遇?请证明: 主要看fast和slow他们之间的每走一步相差的距离,如果相差的距离为1,则一定能追上;如果相差的距离为大于1,要分情况讨论,看他们之间的距离是奇数还是偶数;
二、求环的长度
思路:让slow和fast相遇->slow再走一圈(同时记个数)
三、求入环点
题目链接:
求入环点
方法一:(利用fast走的路程永远是slow的两倍建立等式)
假设:
起始点~入口点距离为L;
入口点~相遇点距离为X(0<=X<C);(slow在相遇之前不可能走完一圈,因为fast比slow快,所以X<C;也有可能slow和fast在入口点相遇,所以X也可以是0)
环的周长:C;
slow走过的距离为L+X;
fast走过的距离为L+nC+X;
成立等式:fast走过的距离是slow的两倍=》L+nC+X=2(L+X)=》整理得:L=nC-X
结论:一个指针从起始点走,一个指针从入口点走,会在入口点相遇
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head)
{
struct ListNode *fast=head;
struct ListNode *slow=head;
struct ListNode *meet=NULL;
struct ListNode *rhead=head;
//找相遇点
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
//一个指针从起始点走,一个指针从相遇点走
meet=fast;
while(meet!=rhead)
{
meet=meet->next;
rhead=rhead->next;
}
return meet;
}
}
return NULL;
}
方法二:(找入口点,转换成找链表交点(链表相交问题))
相遇点跟相遇点的下一个节点直接断开;
一个指针从起始点开始走算长度,一个指针从相遇点的下一个节点开始走算长度,然后再让长的先走,相交的那个点就是入口点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
int Compare(struct ListNode *circle,struct ListNode *rhead)
{
int d1=0;
int d2=0;
while(circle)
{
circle=circle->next;
d1++;
}
while(rhead)
{
rhead=rhead->next;
d2++;
}
return d1-d2;
}
struct ListNode *detectCycle(struct ListNode *head)
{
struct ListNode *fast=head;
struct ListNode *slow=head;
struct ListNode *meet=NULL;
//找相遇点
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
//断开结点
slow=slow->next;
fast->next=NULL;
//判断哪一段长
struct ListNode *rhead=head;
struct ListNode *circle=slow;
int ret=Compare(circle,rhead);
fast=circle;
slow=rhead;
if(ret<0)
{
fast=rhead;
slow=circle;
}
//长的先走
int distance=abs(ret);
while(distance)
{
fast=fast->next;
distance--;
}
//一起走
while(fast!=slow)
{
fast=fast->next;
slow=slow->next;
}
return fast;
}
}
return NULL;
}