整理自剑指Offer
一:题目描述
一个链表中包含环,请找出该链表的环的入口结点。
二:解题思路
链表中有环,情况如图所示:
可以用两个指针解决这个问题
第一步:指针p1,p2指向链表的头结点。
第二步:如果结点中的环有n个结点,指针p1先在链表上向前移动n步,然后两个指针以相同的速度向前移动
第三步:当第二个指针指向环的入口时,第一个指针已经围绕环走了一圈又回到入口结点(即p1=p2)
过程如下图:
现在要解决,如何获得环中结点数目。如果我们能获得环内的一个结点,那么从该节点出发回到该节点的长度就是环的长度。那么问题又变成求环内的一个结点。
可以采用现实生活中,跑步扣圈的思想,如果一个跑的快,一个跑的慢,那么快的一定会在一定时间追上慢的。
利用一快一慢的指针,慢指针每次走一步,快指针每次走两步,如果链表中存在环,则快慢指针相等的结点,一定就在环内。
如下图:
梳理一下思路:
1.找到环内的任意一个结点(采用一快一慢指针)
2.根据环内的结点,求出环的长度
3.利用两个指针,一个指针先走环的长度,然后两个指针一起走,相等的结点即是环的入口。
三:代码实现
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
//找链表环中一个结点,无环则返回空
ListNode* NodeInHoop(ListNode* pHead){
//边界条件
if(pHead==NULL)
return NULL;
//链表不空,两种情况 1有环 2无环
//利用一快一慢指针,快的指针走两步,慢的指针走1步
ListNode* pSlow=pHead->next;
if(pSlow==NULL){
return NULL;
}
ListNode* pFast=pSlow->next;
//有环就不会指向NULL
while(pFast!=NULL && pSlow!=NULL){
//如果两个指针相等,链表有环,两指针指向环中的一个结点
if(pFast==pSlow)
return pFast;
//不相等,慢指针移动1位,快指针移动2位
pSlow=pSlow->next;
pFast=pFast->next;
if(pFast!=NULL)
pFast=pFast->next;
}
//无环,最终的next会指向NULL
return NULL;
}
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
//边界条件
if(pHead==NULL)
return NULL;
//查找环中的一个结点
ListNode* pNodeInHoop=NodeInHoop(pHead);
//无环
if(pNodeInHoop==NULL)
return NULL;
//有环
//计算环的长度:以该结点开始遍历,回到该结点,获得环的长度
ListNode* pNode1=pNodeInHoop;
int lenHoop=1;
while(pNode1->next!=pNodeInHoop){
lenHoop++;
pNode1=pNode1->next;
}
ListNode* pNode2=pHead;
pNode1=pHead;
//将pNode1先移动lenHoop
for(int i=1;i<=lenHoop;i++){
pNode1=pNode1->next;
}
while(pNode1!=pNode2){
pNode1=pNode1->next;
pNode2=pNode2->next;
}
return pNode1;
}
};