题目:判断单链表是否有环,如果有则找出环的起点。
这是另一道题的扩展,利用快慢指针判断出是否有环后,还需要找出环的起点,分析如下:
- 设链表长度为len(链表中非空next指针的个数,下面所说的长度均为非空next指针的个数),链表head到环的起点长度为a,环起点到快慢指针相遇点的长度为b,环的长度为r。
- 假设到快慢指针相遇时,慢指针移动的长度为s,则快指针移动长度为2s,而快指针移动的长度还等于s加上在环上绕的k圈(k>=1),所以2s=s+kr ,即s = kr。
- 由s = a + b 和 s = kr 可知 a + b = kr = (k-1)r + r; 而r = len - a,所以a + b = (k-1)r + len - a, 即 a = (k-1)r + len - a - b,len - a - b是相遇点到环的起点的长度,由此可知,从链表头到环起点长度 = (k-1)环长度+从相遇点到环起点长度,于是我们从链表头、与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇点为环起点。
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
// IMPORTANT: Please reset any member data you declared, as
// the same Solution instance will be reused for each test case.
if(head == NULL || head->next == NULL)return NULL;
ListNode *fast = head, *low = head;
while(fast != NULL && fast->next != NULL)
{
low = low->next;
fast = fast->next->next;
if(low == fast)break;
}
if(low == fast)
{//寻找环的起点
fast = head;
while(fast != low)
{
fast = fast->next;
low = low->next;
}
return low;
}
else return NULL;
}
};
9.19更新
--------------
2. 如果有环,如何判断环的起始点。
假设linked list从head到环起始点s的长度为L,环的周长为C(两节点之间的长度为之间link的数量)
当fast与slow第一次相遇的位置记为m1,并假设m1离开环起始点s距离X,由于fast走的总路程一定是slow的两倍:
(L + X)*2 = L + n*C + X => L = n*C - X
从m1出发,走n*C - X的路程将回到s,而这段路程正好等于head到s之间的路程!所以第一次相遇后,将slow移到head,然后slow/fast同时以一次走一步的速度前进,直到它们第二次相遇,便是s了。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if (!head||!head->next) {
return NULL;
}
ListNode* fast = head, *slow = head;
while (fast) {
if (!fast->next) {
return NULL;
}
fast = fast->next->next;
slow = slow->next;
if (slow==fast) {
break;
}
}
if (!fast) {
return NULL;
}
slow = head;
while (slow!=fast) {
slow = slow->next;
fast = fast->next;
}
return slow;
}
};