题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
算法分析
当用一个指针难以解决问题时,可以尝试用多个指针分析。
假如我们能够知道环上结点的数目 n , 设置指针 pNode1 和 pNode2,pNode2 先走 n 步,之后 pNode1 和 pNode2 开始遍历,当 pNode1 == pNode2 时,即为入口节点。
具体来讲:
- 设置两个指针 p1 和 p2,每次 p1 走一步, p2 走两步,如果存在环,则 p1 和 p2 在若干步操作后就会相遇。此步骤可以判断是否有环存在
- 分析环存在与否对应的解决方案
- 当没有环存在时,返回 nullptr;
- 当有环存在时:
- 首先确定环上结点的个数,比如图3.8中结点个数为4:具体来讲,从 p1 和 p2 相遇的地址开始,向后遍历并累计结点数目 n,直到再次回到相遇的地址。
- 寻找入口:设置指针 pNode1 和 pNode2,pNode2 先走 n 步,之后 pNode1 和 pNode2 开始遍历,当 pNode1 == pNode2 时,即为入口节点。
程序代码
切勿忘记代码的鲁棒性,即要对特殊情况予以特殊处理。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if(pHead == nullptr)
return nullptr;
ListNode* rep = IsOfLoop(pHead);
unsigned int Count = 1;
if(rep == nullptr)
return nullptr;
else
{
ListNode* pNode1 = pHead;
ListNode* pNode2 = pHead;
unsigned int NodeStep = 0;
ListNode* findp = rep;
//确定环的节点个数
while(findp->next != rep)
{
Count++;
findp = findp->next;
}
//pNode2移动Count个节点
while(NodeStep < Count)
{
pNode2 = pNode2->next;
NodeStep++;
}
//确定入口
while(pNode1 != pNode2)
{
pNode1 = pNode1->next;
pNode2 = pNode2->next;
}
return pNode1;
}
}
ListNode* IsOfLoop(ListNode* pHead)
{
ListNode* p1 = pHead;//慢指针
ListNode* p2 = pHead;//快指针
while(1)
{
int step = 0;
while(step < 2)
{
p2 = p2->next;
step++;
if(p2 == nullptr)
return nullptr;
}
p1 = p1->next;
if(p1 == p2)
return p1;
}
}
};
总结:
多指针来解决链表的复杂问题