题目:一个链表中包含环,如何找出环的入口结点?例如下图的链表中,环的入口结点是结点3。
思路:受面试题15的启发,可以用两个指针来解决这个问题。定义p1和p2两个指针,均指向链表的头结点,如果链表中的环有n个结点,指针p1现在链表上向前移动n步,然后两个指针以相同的速度向前移动。当第二个指针指向环的的入口结点时,第一个指针已经围绕着环走了一圈又回到了入口结点。为什么是p1先走n步?书上这里略过没讲,这里证明一下:假设有一个指针p指向链表头结点,设不在环中的结点个数为m,那么指针p走m步即可第一次到达入口结点,例如图中指针先走两步即可到达入口结点。指针继续向后走,再走n步,因为环的最后一个结点指向了入口结点,又回到了入口结点,所以第二次达到入口结点总共需要m+n步,这才有两个指针的时候p1需要先走n步。
现在关键的问题是求出环的结点个数,是想一下,如果我们的结点已经在环中,那么走一圈还会回到刚开始的结点,这样就可以统计环中结点的个数。问题又转化为找出任意一个环中的结点。环形链表有什么性质?那就是如果定义两个指针,一个指针每次走两步,另一个指针每次走一步,最终它们会再次相遇,所以相遇的那个结点就是我们要找的环内的结点,问题解决!
ListNode* MeetingNode(ListNode* pHead)
{
if(pHead == NULL)
return NULL;
ListNode* p1 = pHead;
ListNode* p2 = pHead;
do
{
p1 = p1->m_pNext;
p2 = p2->m_pNext;
if(p2 != NULL)
p2 = p2->m_pNext;
if(p1 == p2)
return p1;
}
while(p1 != NULL && p2 != NULL);
return NULL;
}
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if(pHead == NULL)
return NULL;
ListNode* nodeInLoop = MeetingNode(pHead);
int sizeOfLoop = 1;
if(nodeInLoop)
{
ListNode* pNode = nodeInLoop->m_pNext;
while(pNode != nodeInLoop)
{
pNode = pNode->m_pNext;
++sizeOfLoop;
}
ListNode* p1 = pHead;
ListNode* p2 = pHead;
for(int i = 0; i < sizeOfLoop; ++i)
p1 = p1->m_pNext;
while(p1 != p2)
{
p1 = p1->m_pNext;
p2 = p2->m_pNext;
}
return p1;
}
return NULL;
}
解法二:
ListNode* CircleStart(ListNode* pHead)
{
if(pHead == NULL)
return NULL;
ListNode* fastPointer, *slowPointer;
fastPointer = slowPointer = pHead;
int CircleNum = 0;
do
{
fastPointer = fastPointer->m_pNext->m_pNext;
slowPointer = slowPointer->m_pNext;
++CircleNum;
if(fastPointer == NULL || fastPointer->m_pNext == NULL)
return NULL;
}
while(fastPointer != slowPointer);
slowPointer = pHead;
while(fastPointer != slowPointer)
{
fastPointer = fastPointer->m_pNext;
slowPointer = slowPointer->m_pNext;
}
return fastPointer;
}