Given a linked list, return the node where the cycle begins. If there is no cycle, return null
.
Note: Do not modify the linked list.
我的AC,也是最直白的(6ms,最快一批):
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode *fast, *slow;
fast = slow = head;
while (fast && fast->next){
slow= slow->next;
fast = fast->next->next;
if (fast == slow)
break;
}
if (fast == NULL || fast->next ==NULL)
return NULL;
fast = head;
while(fast != slow){
slow= slow->next;
fast = fast->next;
}
return slow;
}
简洁版:
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode *fast, *slow, *ans;
fast = slow = head;
ans = NULL;
while (fast && fast->next){
slow= slow->next;
fast = fast->next->next;
if (fast == slow){
ans = head;
while(ans != slow){
slow = slow->next;
ans = ans->next;
}
break;//这个break很关键 别落
}
}
return ans;
}
分析:
判断一个链表是否有环,有则返回环入口点,否则返回NULL。
H________A____B
|____|
D C
slow从H走到A时,fast一定在环上某处C(或B或D)。设此时slow走过了HA=x,fast前方距环入口点距离CDA=y,环周长为r。
当fast若与slow相遇时,slow肯定没有走遍历完链表,而fast已经在环内循环了n圈(1<=n)。
设环长为r,起点到环入口点的距离为a,入口环与相遇点距离为x,相遇点再到入口处距离为y。
slow步数 = a+x
fast步数 = a + nr + x
fast是slow的二倍,则有:a+nr+x = 2*(a+x)
整理得nr = a+x,移项有:
a = nr - x
= (n-1)r +(r-x)
= (n-1)r + y
y为相遇点到环入口点的距离。由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点。于是我们从链表头、相遇点 这两点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。
类似的解释:
|------ n ------|
H____h____A__c__Meet
|_____|r
在p2和p1第一次相遇的时候,假定p1走了n步,环路的入口是在h步的时候经过的,那么有
p1走的路径: h+c = n; c为p1和p2相交点,距离环路入口的距离
p2走的路径: h+c+k*r = 2*n; r为环路的周长,k是整数
显然,如果从h+c点开始,p1再走n步骤的话,还可以回到h+c这个点
同时p2从头开始走的话,经过n步,也会达到h+c这点
显然在这个步骤当中p1和p2只有前h步走的路径不同,所以当p1和p2再次重合的时候,必然是在链表的环路入口点上。
解析整理自:http://blog.chinaunix.net/uid-26448049-id-3046656.html