题目:
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
链表的结构如下图:
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
这个题目简洁明了,完全不做作。一看这个题目有两个思路:
(1)标记法
非常简直直接的思路,就是记录一下走过的路径,如果碰到已经走过的路径,那么就是说碰到了环路的就说明找到了环路的入口,
程序:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
set<ListNode*> map;
while(pHead!=NULL){
auto temp=map.insert(pHead);
if(temp.second==false)
return pHead;
pHead=pHead->next;
}
return NULL;
}
(2)快慢指针法
这个思路就不是那么的直接了,就是一开始设置两个指针,一个慢指针,一个快指针。快指针每次走两步,慢指针每次走一步,如果有环路,那么快慢指针一定会相遇,因为会进入环路,会一直在环路里面你追我赶,一定会相遇的。
让相遇的时候,设置两个慢指针,一个从表头,一个从相遇的点继续出发,每次走一步,那么相遇的的地方就是环的入口。
论证:(1)有环路,就一定会相遇。
(2)假设表头到环入口的长度为a ,环入口到相遇点为b ,相遇点到环入口长度为c
当相遇的时候,快指针=a+(b+c)*k+b,期中k是走了k圈,k>=1;
蛮指针=a+b;
由于蛮指针走的比快指针慢1/2, 所以有:(a+b)*2=a+(b+c)*k +b
所以:a=(k-1)*(b+c)+c , 所以说a到环入口的长度是 相遇点到环入口的长度加上环周长的整数倍
所以两个指针同时从环起点和相遇点开始跑,那么久一定可以在环的入口相遇的。
代码:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
ListNode* Fast=pHead;
ListNode* Slow=pHead;
while(Fast!=NULL){
//相遇点
Slow=Slow->next;
Fast=Fast->next;
if(Fast==NULL)
return NULL;
Fast=Fast->next;
if(Fast==Slow){
break;
}
}
if(Fast==NULL)
return NULL;
//从开始和相遇点同时出发
Fast=pHead;
while(Fast!=Slow){
Fast=Fast->next;
Slow=Slow->next;
}
return Slow;
}