题目:
如果一个链表中包含环,如何找出环的入口节点?例如:在下图的链表中,环的入口节点是节点3。
解题思路
- 第一步,如何确定链表中包含环:
可以利用快慢指针法,确定链表是否带环。定义两个指针,同时从链表的头节点出发,然后同时往后走,快指针一次走两步,慢指针一次走一步。如果两指针相遇,则链表包含环;如果快指针走到链表末尾也没有相遇,那么链表就不包含环。 - 第二步:如何得到环中节点的数目:
在第一步使用快慢指针法时,如果两个指针相遇,则表明链表中存在环,并且两个指针相遇的节点一定在环中。随后,我们可以从这个节点出发,一边继续向前移动一边计数,当再次回到这个节点时,就可以得到环中节点数了。(这步可以省略,下面证明) - 第三步,如何找到环的入口节点:
可以定义两个指针P1和P2指向链表的头节点。如果链表中的环有n个节点,指针P1先在链表上向前移动n步,然后两个指针以相同的速度向前移动。当指针P2指向的入口节点时,指针P1已经围绕着环走了一圈,又回到了入口节点,它们相遇的节点正好是环的入口节点。
第二步可以省略的原因(即快慢指针相遇的节点,距离头节点的节点数,就是环中节点数目):
因为快慢指针相遇时,快指针比慢指针多走了一个环的跳数,也就是环中节点的数目。而快指针一次走两步,慢指针一次走一步,快指针跳数一直是慢指针两倍,此时快指针与慢指针的跳数差等于慢指针的跳数,即当前节点距离头节点的跳数,所以此时相遇节点的位置,满足第三步的要求。
2.代码:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution{
public:
ListNode* EntryNodeOfLoop(ListNode* pHead){
if(!pHead) return NULL;
//慢指针
ListNode* p1 = pHead;
//快指针
ListNode* p2 = pHead;
//判断快指针是否到达末尾
while(p2 != NULL && p2->next != NULL){
p1 = p1->next;
p2 = p2->next->next;
//如果两者相遇,说明链表包含环(第一步)
if(p1 == p2){
//慢指针返回头节点,此时快指针刚好比慢指针多走n步
//n为环节点数目
p1 = pHead;
//第三步
while(p1 != p2){
p1 = p1->next;
p2 = p2->next;
}
//入口节点
return p1;
}
}
return NULL;
}
};