链表带环的问题研究及代码实现
[TOC]
完整源代码见:
https://github.com/lankuohsing/DataStructureInCPP/tree/master/DataStructureInCPP/Linked_List_with_Loop
注:如无特别说明,本文中的链表均含有附加表头结点first
1. 如何判断链表是否有环?
思路是让两个指针slow和fast同时从链表头出发遍历链表,fast的速度是slow的两倍(为简单起见我们可以让slow每次走一个节点,fast每次走两个节点),若slow和fast相遇,则说明有环。
代码如下:
bool::Linked_List_with_Loop::isLoop ( )
{
LinkNode*slow, *fast;
slow = fast = first->link;
if ( fast==NULL )
{
return false;
}
while ( fast->link&&fast->link->link )
{
slow = slow->link;
fast = fast->link->link;
if ( slow==fast )
{
return true;
}
}
}
2. 如何计算环的长度?
先给答案:第一次相遇(超一圈)时开始计数,第二次相遇时停止计数。
具体推导如下:
如上图所示,设链表总长为L,链表头距离环的入口长度为a,环的入口点距离相遇点长度为x,环的长度为R
设slow的速度为v,fast的速度为2v。无论它们在环上哪一点相遇,它们再次相遇所需时间为R/(2v-v)=R/v。到下一次相遇时,slow走的距离刚好为R,也即得到环的长度。
代码如下:
int Linked_List_with_Loop::getLoopLength ( )
{
LinkNode*slow, *fast;
slow = fast = first->link;
int loopLength = 0;
if ( fast == NULL )
{
return loopLength;
}
bool isMeet = false;
while ( fast->link&&fast->link->link )
{
slow = slow->link;
fast = fast->link->link;
if ( slow == fast )
{
isMeet = true;
}
if ( isMeet )
{
loopLength++;
if ( slow->link == fast->link->link )
{
break;
}
}
}
return loopLength;
}
3. 如何得到环的入口点
先给答案:让两个指针分别从表头和相遇点开始走,速度一样,均为每次走一个节点,相遇时的那个点就是连接点。
首先我们证明,当slow和fast相遇时,slow还没走完环的一圈,而fast已经走了超过一圈的路程。证明如下:
假设当slow刚到入口点时,fast已经走了nR+b(
n≥0,,0≤b<R
,n和b不同时为0)。当b=0时,说明slow和fast在入口点相遇,这时slow显然还没走完环的一圈,fast走完了大于1的整数圈;当
0<b<R
时,fast追上slow的时间为(R-b)/v,追及过程中slow走的距离为R-b,也即说明slow还没走完一圈,而fast走过的距离为2R-2b,加上之前走的nR+b,总共走完了环上的距离为(2+n)R-b>R。得证。
进一步我们可以得到相遇点距离入口点的长度为b
第一次相遇时,slow走了s=a+x步,fast走了2s=s+nR步,也即a+x=nR,也即a=(n-1)R+R-x,也即说明如果此时两个指针分别从表头和相遇点同时同速走,一定会在环的入口点相遇。
代码如下:
LinkNode * Linked_List_with_Loop::getLoopStartNode ( )
{
LinkNode*slow, *fast;
LinkNode * loopStartNode = NULL;
slow = fast = first->link;
if ( fast == NULL )
{
return NULL;
}
bool isMeet = false;
while ( fast->link&&fast->link->link )
{
slow = slow->link;
fast = fast->link->link;
if ( slow == fast )
{
isMeet = true;
break;
}
}
if ( isMeet )
{
fast = first->link;
while ( true )
{
slow = slow->link;
fast = fast->link;
if ( slow == fast )
{
loopStartNode = slow;
break;
}
}
}
return loopStartNode;
}