题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
方法一:
通过set STL 实现
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
set<ListNode*> lnSet;
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if (!pHead)
return NULL;
ListNode* pointer = pHead;
while (pointer) {
if (lnSet.count(pointer) == 0){
lnSet.insert(pointer);
pointer = pointer->next;
} else
return pointer;
}
return NULL;
}
};
方法二:
链接:https://www.nowcoder.com/questionTerminal/253d2c59ec3e4bc68da16833f79a38e4
来源:牛客网
两个结论:
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 {
set<ListNode*> lnSet;
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if (!pHead)
return NULL;
ListNode* fastP = pHead;
ListNode* slowP = pHead;
while (fastP && slowP->next) {
fastP = fastP->next->next;
slowP = slowP->next;
if (fastP == slowP) {
fastP = pHead;
while (fastP != slowP) {
fastP = fastP->next;
slowP = slowP->next;
}
return fastP;
}
}
return NULL;
}
};
方法三:
断链法
//链接:https://www.nowcoder.com/questionTerminal/253d2c59ec3e4bc68da16833f79a38e4
//来源:牛客网
/*
时间复杂度为O(n),两个指针,一个在前面,另一个紧邻着这个指针,在后面。
两个指针同时向前移动,每移动一次,前面的指针的next指向NULL。
也就是说:访问过的节点都断开,最后到达的那个节点一定是尾节点的下一个,
也就是循环的第一个。
这时候已经是第二次访问循环的第一节点了,第一次访问的时候我们已经让它指向了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;
}
};