一、题目
一个链表中包含环,如何找出环的入口结点?例如,在图的链表中,环的入口结点是结点3。
图. 节点3是链表环的入口节点
二、解法
分析:解决这个问题第一步是如何确定一个链表中包含环。我们可以定义两个指针,同时从链表头结点出发,一个指针一次走一步,另一个指针一次走两步。如果走的快的指针追上了走得慢的指针,那么链表就包含环;如果走的快的指针走到了链表的末尾都没有追上第一个指针,那么链表就不包含环。
第二步是如何找到环的入口,我们还是可以定义两个指针P1和P2指向链表的头结点,如果链表中的环有n个节点,则指针P1先在链表向前移动n步,然后两个指针以相同的速度向前移动,当第二个指针指向环的入口节点时,第一个指针已经环绕着环走了一圈,又回到了入口节点。剩下的问题是如何得到环中节点的数目,我们在前面提到判断一个链表是否有环时用到了一快一慢两个指针。如果两个指针相遇,则表明链表中存在环。两个指针相遇的节点一定在环中,可以从这个节点出发,一边继续向前移动一边计数,当再次回到这个节点时,就可以得到环中节点数了。
ListNode* MeetingNode(ListNode* pHead)
{
if(pHead == nullptr)
return nullptr;
ListNode* pSlow = pHead->m_pNext;
if(pSlow == nullptr)
return nullptr;
ListNode* pFast = pSlow->m_pNext;
while(pFast != nullptr && pSlow != nullptr)
{
if(pFast == pSlow)
return pFast;
pSlow = pSlow->m_pNext;
pFast = pFast->m_pNext;
if(pFast != nullptr)
pFast = pFast->m_pNext;
}
return nullptr;
}
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
ListNode* meetingNode = MeetingNode(pHead);
if(meetingNode == nullptr)
return nullptr;
// 得到环中结点的数目
int nodesInLoop = 1;
ListNode* pNode1 = meetingNode;
while(pNode1->m_pNext != meetingNode)
{
pNode1 = pNode1->m_pNext;
++nodesInLoop;
}
// 先移动pNode1,次数为环中结点的数目
pNode1 = pHead;
for(int i = 0; i < nodesInLoop; ++i)
pNode1 = pNode1->m_pNext;
// 再移动pNode1和pNode2
ListNode* pNode2 = pHead;
while(pNode1 != pNode2)
{
pNode1 = pNode1->m_pNext;
pNode2 = pNode2->m_pNext;
}
return pNode1;
}