①本题思路
快慢双指针工作方式:
slow和fast从同一起点出发。(注意:同一起点就是链表头结点出发!)
fast每次右移两个单位长度
slow每次右移一个单位长度
1.判断是否有环
成环:slow和fast相遇
不成环:fast最终指向NULL
2.寻找入环点(难)
难点是由于需要进行一些数学推导,找出等量关系。
简略的数学推导一下:
Ⅰ:展示原始环形链表
Ⅱ:两点把环形链表分成了三个区域设出x,y,z
Ⅲ:明确推导目标
Ⅳ:列出等量关系(三条),并整合得出x的等式
①:2slow = fast。
(fast、slow在此都表示走过的长度)
②:X + n(y+z) + y= fast。
描述fast走过的距离为:x + n(y+z) + y
n表示转的圈数,取值范围 [1,INF),
理解圈数:x很大很长,环形很小(y+z值很小),相遇之前fast必然在圈内循环!(即slow指针到达x前)
而必须是循环一圈,才能够使得fast和slow相遇,所以n取值范围最小值是1
③:slow = x+y
描述slow指针走过的举例为x+y
最终整合三个等式得到:x = (n-1)(y+z)+z
整合方式:②③式代入①式
Ⅴ:最终表达式(找到入口点x)
x = (n-1)(y+z)+z
这条表达式如同:
有两个指针分别从:O点,B点以相同的速度出发,最终将在A相遇。
(z可能比x长,当出现这种情况就会在环形内循环,直到总路程一样总能在x处相遇)
②代码实现
其实结论很简单,知道结论做题犹如知道答案考试,几下就能写完代码的,时间主要都是花在公式推导,以及初次接触很难想到!
第一种代码:(因为经常使用while,所以给了while版本)
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head==NULL||head->next==NULL)return NULL;
ListNode *fast = head;
ListNode *slow = head;
//可以变成dowhile语句———— (第二种版本)
if(fast==NULL||fast->next==NULL)return NULL;
fast=fast->next->next;
slow=slow->next;
//可以变成dowhile语句———— (第二种版本)
while(fast!=slow){//因为一开始fast和slow指向同一个位置,所以上面要先移动一次//适合do...while语句其实!
if(fast==NULL||fast->next==NULL)return NULL;//不成环(fast是右移两次,所以判空方式不同)
fast=fast->next->next;//
slow=slow->next;
}
//代码到这里,slow和fast就相遇了!
//接下来就是根据表达式,两个变量从相遇点出发,相遇的时候就是出口点
ListNode *first = head;
ListNode *second = fast;//使用slow复制也行,因为一样!
while(first!=second){
first=first->next;
second=second->next;
}
return first;//返回second也行
}
};
第二种版本:do…while语句
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head==NULL||head->next==NULL)return NULL;
ListNode *fast = head;
ListNode *slow = head;
do{//因为一开始fast和slow指向同一个位置,所以上面要先移动一次//适合do...while语句其实!
if(fast==NULL||fast->next==NULL)return NULL;//不成环(fast是右移两次,所以判空方式不同)
fast=fast->next->next;//
slow=slow->next;
}while(fast!=slow);
//代码到这里,slow和fast就相遇了!
//接下来就是根据表达式,两个变量从相遇点出发,相遇的时候就是出口点
ListNode *first = head;
ListNode *second = fast;//使用slow复制也行,因为一样!
while(first!=second){
first=first->next;
second=second->next;
}
return first;//返回second也行
}
};