链表中环的入口结点
题目:如果一个链表中包含环,如何找出环的入口点?例如:下图所示的链表,环的入口结点是结点3
解决这个问题的第一步是如何确定一个链表中包含环。
一个包含环的链表,一直向前遍历是无穷次的。那么我们可以用两个快慢指针来解决这个问题。
定义两个指针,一个一次走一步,另一个一次走两步。如果快指针追上了慢指针,那么表示链表有环。如果快指针到达链表尾部(next域为空),说明链表无环。
确定链表有环之后,我们接下来就是找环的入口了。
我们可以用两个指针来解决这个问题。先定义两个指针p1和p2指向链表的头结点。如果链表中的环有n个结点,则p1现在链表上向前移动n步,然后两个指针以相同的速度,一次向前移动一步,当第二个指针指向环的入口结点时,第一个指针已经围绕着环走了一圈,又回到了入口结点。这种思路类似于找链表中倒数第k个结点的思路,入口结点是第k个结点,也是尾部结点。
而如何求环中有几个结点呢?
我们在前面确定环是否存在时,快慢指针相遇的位置必定在环内。我们只需再定义一个指针,在环内绕一圈,即可统计环内结点的个数。
代码示例:
//找相遇结点,无环返回空指针
HeadList* MeetingNode(HeadList* head)
{
if (head == NULL) exit(0);
HeadList* pSlow = head->next;
HeadList* pFast = NULL;
if (pSlow)
pFast = head->next->next;
while (pSlow != NULL && pFast != NULL && pFast->next != NULL)
{
if (pSlow == pFast)
return pSlow;
pSlow = pSlow->next;
pFast = pFast->next->next;
}
return NULL;
}
//找环入口结点
HeadList* EntryNodeOfLoop(HeadList* head)
{
HeadList* meetingNode = MeetingNode(head);
if (meetingNode == NULL) return NULL;
int nodeInLoop = 1;
HeadList* p1 = meetingNode;
while (p1->next != meetingNode)
{
p1 = p1->next;
nodeInLoop++;
}
p1 = head;
for (int i = 0; i < nodeInLoop; ++i)
p1 = p1->next;
HeadList* p2 = head;
while (p1 != p2)
{
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
测试代码:带头结点单链表的C语言代码实现