这个问题在面试中是老生常谈的问题了,而且,解决这类问题的主要方法就是追击法,因此,这篇博客中只讲解这种方法求解环的问题。
这里给出三个链表中环的问题:
- 判断链表中是否存在环;
- 如果有环,求出环的入口;
- 如果有环,求出环长。
这里给出一副图片,所有的讨论都在这图片上进行。
问题一:判断链表是否有环
这个问题很好解决,设置两个指针ptr_1, ptr_2
在A处,让ptr_1
以1的速度往后走(即每次移动一个位置),ptr_2
以2的速度往后走,如果链表中存在环,那么ptr_1, ptr_2
都会进入环中,ptr_2
会以2的速度追击ptr_1
,而且ptr_2
一定可以追上ptr_1
(即ptr_2 == ptr_1
);如果链表中不存在环,那么ptr_1
和ptr_2
之中一定有一个等于空指针。
bool judge_the_circle(ListNode *pHead)
{
ListNode *ptr_1, *ptr_2;
for (ptr_1 = ptr_2 = nullptr;
ptr_1 != nullptr && ptr_2->next != nullptr && ptr_1 != ptr_2;
ptr_1 = ptr_1->next, ptr_2 = ptr_2->next->next) {}
return ptr_1 != nullptr && ptr_1 == ptr_2;
}
问题二:如果有环,求出环的入口
还是上面那幅图片,问题一中讲到如果有环,ptr_1
和ptr_2
一定会相遇,但是相遇在哪个地方呢?是不是没有思考过这个问题,要想知道相遇在哪个地方,还要从这两个指针的速度入手。
与上个问题一样的做法,只不过这次我要分一下运动的阶段了:
- 首先,两个指针都从A点出发,当
ptr_1
走到B点时,ptr_2
走到了C点,由于ptr_2
的速度是ptr_1
的2倍,所以a
段长度等于b
段; - 这个时候,两个点都进入了环中,并且以后会顺时针一直移动,
ptr_2
在C点以2的速度追击ptr_1
。ptr_2
以ptr_1
为参照物,那么ptr_2
相对于ptr_1
的速度为1,也就是说,ptr_2
要以1的速度来走完C - D
段(b
段等于c
段)加c
段的距离才能与ptr_1
相遇,这个时候,ptr_2
还是以ptr_1
为参照物,我们再换个视角,在ptr_2
追击ptr_1
的这段时间中,ptr_1
走到哪了?ptr_1
的速度为1,和ptr_2
的相对速度一样,那么ptr_1
走的路程就和ptr_2
走的相对路程一样,故ptr_1
在这段时间从B点走了C - D
段加c
段的距离,又b
段等于c
段,所以,两个指针在D点相遇; - 通过上面这个阶段知道了相遇点在D点,又
a
段等于b
段,b
段等于c
段,故a
段等于c
段, 这个时候,让ptr_2
到A点去,以1的速度往后走,ptr_1
还在D点,然后两个指针同时走,再次相遇时,相遇点就是B点,也就是环的入口。
上面数学分析的既是算法的原理,也是算法的步骤,因此可以很清楚地写出代码。
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
ListNode *ptr_1 = pHead, *ptr_2 = pHead;
if (!(ptr_1 != nullptr && ptr_2 != nullptr && ptr_2->next != nullptr))
return nullptr;
do {
ptr_1 = ptr_1->next;
ptr_2 = ptr_2->next->next;
} while (ptr_1 != nullptr && ptr_2 != nullptr &&
ptr_2->next != nullptr && ptr_1 != ptr_2);
if (!(ptr_1 != nullptr && ptr_2 != nullptr && ptr_2->next != nullptr))
return nullptr;
for (ptr_1 = pHead; ptr_1 != ptr_2; ptr_1 = ptr_1->next, ptr_2 = ptr_2->next) {}
return ptr_1;
}
问题三:如果有环,求出环长
理解了问题二,这个问题就很简单了,调用问题二的函数,求出环的入口,然后让ptr_1
走到环的入口,然后让ptr_1
再次走到环的入口,统计下路程,就是环长。
int lengthOfLoop(ListNode* pHead)
{
ListNode *enterNode = EntryNodeOfLoop(pHead);
int ret = 0;
do {
++ret;
} while ((ptr = ptr->next) != enterNode);
return ret;
}
这类问题慢慢分析就好了,多画图。