链表中环的入口结点
时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
思路:
设置快慢指针都从链表头出发,快指针每次走两步,慢指针每次走一步,假如有环,一定可以相遇于环中某点(结论1)。接着让两个指针分别从相遇点和链表头出发,两者都改为每次走一步,最终相遇于环入口(结论2)。对上述两个结论,以下进行了证明:
两个结论:
1.设置快慢指针,假如有环,最终他们必相遇。
2.两个指针分别从链表头和相遇点继续出发,每次走一步,最终一定相遇于环入口。证明结论1:设置快慢指针fast和low,fast每次走两步,low每次走一步。假如有环,两者一定会相遇(因为low一旦进入环中,可视为fast在后面追赶low的过程,每次两者都接近一步,最后一定能相遇)。
证明结论2:
设:
链表头到环入口长度为a,
环入口到相遇点长度为b,
相遇点到环入口长度为c
则:相遇时
快指针路程 = a+(b+c)k+b,k>=1 其中b+c为环的长度,k为绕环的圈数(K >= 1,即最少一圈,不能是0圈,不然和慢指针走到一样长,矛盾)
慢指针路程 = a + b
快指针走的路程是慢指针的两倍,所以:
(a + b) * 2= a+(b+c)k + b
化简可得:
a = (k-1)(b+c)+c,即链表头到环入口的长度等于相遇点到环入口的距离+(k-1)倍的环长度。其中K>=1,所以k-1>=0圈,所以两个指针分别从链表头和相遇点出发,每次走一步,最终一定相遇于环入口。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
ListNode* fast = pHead,*low = pHead;
while(fast && fast->next)
{
fast = fast ->next->next;
low = low->next;
if(low == fast)
break;
}
if(!fast || !fast->next)
return NULL;
low = pHead;
while(fast != low)
{
fast = fast->next;
low = low->next;
}
return low;
}
};
还有一种方法,虽然对原本的数据结构进行了破坏,但对本题不乏是一种新奇有效的解法
设立两个指针,一个在左(previous),一个在右(front),previous指针紧跟front指针,每向前移动时就将previous指针的next赋为NULL,此时已经将数据结构损坏,最后将previous所指向的即为所求。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if(!pHead->next)
{
return NULL;
}
ListNode* previous = pHead;
ListNode *front = pHead->next;
while(front)
{
previous->next = NULL;
previous = front;
front = front->next;
}
return previous;
}
};
由于对原始数据结构存在破坏,所以在实际中也许存在一定的争议。