题目:如果一个链表中包含环,如何找出环的入口节点?例如,在下图所示的链表中,环的入口节点是节点3。
(1)判断一个链表是否包含环?
可以用两个指针来解决这个问题,定义一个慢指针和一个快指针,这两个指针同时从链表的头节点出发,慢指针一次走一步,快指针一次走两步,如果快指针追上了慢指针,说明链表就包含环,如果快指针走到了链表的末尾都么追上慢指针,说明链表不包含环。
(2)如何找到环的入口?
定义两个指针p1和p2指向链表的头节点,如果链表中的环有n个节点,则让指针p1先在链表上向前移动n步,然后两个指针以相同的速度向前移动,当指针p2指向环的入口节点时,指针p1已经围绕着环走了一圈,又回到了入口节点。
(3)如何得到环中节点的数目?
在(1)中提到判断一个链表是否包含环时用到了快慢指针,如果两个指针相遇,说明链表中存在环,两个指针相遇的节点一定是在环中,因此可以从这个节点出发,一边继续向前移动一边计数,当在次回到这个节点时,就可以计算出环中的节点数。
以题目中的实例为例,分析两个指针的移动规律:
(1)指针p1和指针p2在初始化时都指向链表的第一个节点,如图1所示;
(2)由于次环中有4个节点,所以指针p1先在链表上向前移动4步,如图2所示;
(3)接下来两个指针以相同的速度在链表上向前移动,直到它们相遇,它们相遇的结点正好是环的入口节点,如图3所示。
具体代码如下所示 :
#include <iostream>
#include <assert.h>
using namespace std;
struct ListNode
{
int value;
ListNode* next;
};
void Init(ListNode* pListHead)
{
assert(pListHead != NULL);
if(pListHead == NULL)
{
return ;
}
pListHead->next=NULL;
}
static ListNode* Buynode()
{
ListNode *pnewnode=new ListNode();
assert(pnewnode != NULL);
pnewnode->next=NULL;
return pnewnode;
}
bool InsertTail(ListNode *pListHead,int val)
{
if(pListHead == NULL)
{
return false;
}
ListNode *pCur=pListHead;
while(pCur->next != NULL)
{
pCur=pCur->next;
}
ListNode *pnewnode=Buynode();
pnewnode->value=val;
pCur->next=pnewnode;
return true;
}
ListNode* MeetingNode(ListNode* pListHead) //判断链表是否存在环,若存在返回环中的一个节点,若不存在环,返回NULL
{
if(pListHead == NULL)
{
return NULL;
}
ListNode *pslow=pListHead->next;
if(pslow == NULL)
{
return NULL;
}
ListNode* pfast=pslow->next;
while(pfast != NULL && pslow != NULL)
{
if(pfast == pslow)
{
return pfast;
}
pslow=pslow->next;
pfast=pfast->next;
if(pfast->next != NULL)
{
pfast=pfast->next;
}
}
return NULL;
}
ListNode *EntryNodeOfLoop(ListNode* pListHead) //找环的入口节点
{
ListNode *meetingNode=MeetingNode(pListHead);
if(meetingNode == NULL) //链表不存在环
{
return NULL;
}
int nodeInLoop=1;
ListNode* pNode1=meetingNode;
while(pNode1->next != meetingNode)
{
pNode1=pNode1->next;
++nodeInLoop; //计算环中的结点数目
}
pNode1=pListHead;
for(int i=0;i<nodeInLoop;++i) //先让pNode1指针先走nodeInLoop步
{
pNode1=pNode1->next;
}
ListNode* pNode2=pListHead;
while(pNode1 != pNode2)
{
pNode1=pNode1->next;
pNode2=pNode2->next;
}
return pNode1; //返回环的入口节点
}
int main()
{
ListNode pListHead;
Init(&pListHead);
for(int i=0;i<6;i++)
{
InsertTail(&pListHead,i+1);
}
ListNode *plast=&pListHead;
while(plast->next != NULL)
{
plast=plast->next;
}
ListNode *pfirst=&pListHead; //pfirst指向头节点
pfirst=pfirst->next; //pfirst->value=1;
pfirst=pfirst->next; //pfirst->value=2;
pfirst=pfirst->next; //pfirst->value=3;
plast->next=pfirst; //让最后一个节点指向第三个节点形成环
ListNode *tmp=EntryNodeOfLoop(&pListHead);
cout<<tmp->value<<endl;
return 0;
}
运行结果如下所示: