题目
传送门:环形链表2
- 样例
分析
- 链表问题多用双指针解决。这里考虑快慢指针。基于如下背景:
- 初始化,slow和fast均指向链表头结点head。
- 指针移动。slow每次移动一步,fast每次移动两步。
这样的设置,使得fast每次相对slow多走一步,并且每一个时刻fast走的长度是slow的两倍。从相对运动的角度看,将slow看做fast的(动态)起点,则若存在环,则fast最终一定会回到这个“起点”,即一定会与slow相遇。
- 先考虑有环的情况。找相遇点是容易的,只需要按照如上规则移动指针并加以判断即可。
假设相遇点已求,挖掘其中的信息
-
首先,假设从head到环入口距离(可以理解为边数)为x,环入口到相遇点的距离为y,相遇点到环入口的距离为t。基于fast每次相对slow多走一步这个条件可列出基本的方程。
-
分类讨论:
– 若x > (y + t),即从head到环入口的距离大于一个环的长度,等慢指针到了入口时,快指针必定已经跑过了n圈(n >= 1),则到达相遇点时,有如下关系:2 ( x + y ) = x + y + n ( y + t ) 2(x+y) = x+y+n(y+t) 2(x+y)=x+y+n(y+t)
即 x = n y − t ( n > 1 ) 即x= ny-t (n > 1) 即x=ny−t(n>1)
– 若x <= (y + t),即从head到环入口的距离小于或等于一个环的长度时,有如下关系:
2 ( x + y ) = x + y + t 2(x+y) = x+y+t 2(x+y)=x+y+t
即 x = y − t 即 x =y-t 即x=y−t
综上,若有环,令p1 = head, p2 = temp(相遇点),则p1与p2第1次相遇时,p1走过的路程必定为x,即为环的入口。
- 若无环,则通过fast指针的遍历可判断出是否无环:考虑无环的情况,若无环,则fast或fast->next最终必定为空。
代码实现
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
/*双指针练习*/
ListNode *slow = head, *fast = head;
//找第一次相遇点
while (fast && fast->next) { //判断有无环
fast = fast->next->next;
slow = slow->next;
if (slow == fast) {
break;
}
}
if (fast && fast->next) { //若用 slow == fast, 则当只有一个结点的时候会判断错误
ListNode *p1 = head;
ListNode *p2 = slow;
while(p1 != p2) {
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
return NULL; //无环
}
};