题目描述:
给定一个链表,若其中包含环,则输出环的入口节点。
若其中不包含环,则输出null
。
样例
给定如上所示的链表:
[1, 2, 3, 4, 5, 6]
2
注意,这里的2表示编号是2的节点,节点编号从0开始。所以编号是2的节点就是val等于3的节点。
则输出环的入口节点3.
分析:
很有意思的题目。难点在于我们无法像遍历图结点那样设置标志数组,第一次访问到已经访问过的点就是环的起点。对于链表,我们没法标识它的某个结点。
本题同样采用双指针,想象一下在操场上绕着环形跑道跑步,只要两个人不停的以不同的速度奔跑,跑的快的总会从后面追上跑得慢的。现在假设有两个指针p和q,p每次前进一步,q每次前进两步,那么某时刻,只要有环,他们必会相遇在某结点。反之,没环的链表,他们一定能遍历到链表的结尾。
盗个图来解释一下。假设上图就是那个带环的链表,a是首节点,b是环的起点。q以p两倍的速度奔跑,某时刻,他们第一次相遇于c点。我们知道,p结点到达c点走了x+y的路(这里的y可能大于环长,不过不影响结果),q到达c点走了2(x+y)的路。当p第一次走到b点时,它走了x长度的路,此时q走了2x的路,其中在环上走了x长度的路,还要走2y才能到达c点,又b到c相距y,也就是这时q还要走y的路才能到达b点,可以推出x+y是环长度的倍数。第一次相遇时q在离b点相距y的位置,再走x距离就可以回到b点。所以第一次相遇后,让p回到a点,p和q从新以相同的速度再次奔跑。p再次到达b点时,恰好q也到达了b点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *entryNodeOfLoop(ListNode *head) {
ListNode *p = head,*q = head;
if(!p || !p->next) return NULL;
while(p && q){
p = p-> next;
q = q->next->next;
if(p == q){
p = head;
while(p != q){
p = p->next;
q = q->next;
}
return p;
}
}
return NULL;
}
};