您的位置:首页 > 其它

判断一个链表是否有环以及环的位置入口

2014-10-16 16:41 701 查看
给定一个单链表,只给出头指针head(指向头节点):

1、如何判断是否存在环?

2、如何知道环的长度?

3、如何找出环的连接点在哪里?

4、带环链表的长度是多少?

5、如何判断两个链表相交?
1,算法的思想是设定两个指针p,
q,其中p每次向前移动一步,q每次向前移动两步。那么如果单链表存在环,则p和q相遇;否则q将首先遇到null。

方法一:

(1)用两个指针p1和p2分别指向表头结点,即p1=p2=head

(2)p1和p2分别采用1和2作为步长遍历该链表。(注意,p2应该检查当前结点的下一个结点是否为NULL)

(3)如果p2遇到了NULL,则证明该链表没有环;若p1和p2在某时刻指向同一结点,则说明该链表有环

方法二:

(a)p从表头结点开始以1为步长遍历表,边遍历边将表反向

(b)如果p遇到NULL,则说明表没有环

(c)如果p最后等于head,则说明表有环,且记此时p所经过的表的结点数为l(l=2l1+c,l1和c的定义见方法一)

(d)p再从表头结点开始以1为步长遍历表,边遍历边反向,当遍历到l/2时,停止,设两个指针p1,p2均指向当前结点,然后分别从两个方向同时以1为步长遍历表(其中一个需要边遍历,边反向链表),当他们第相遇时,当前结点即为环头结点。且此时链表还原成原来的链表。

用两个指针来遍历这个单向链表,第一个指针p1,每次走一步;第二个指针p2,每次走两步;当p2 指针追上 p1的时候,就表明链表当中有环路了。

关于这个解法最形象的比喻就是在操场当中跑步,速度快的会把速度慢的扣圈.

可以证明,p2追赶上p1的时候,p1一定还没有走完一遍环路,p2也不会跨越p1多圈才追上.

我们可以从p2和p1的位置差距来证明,p2一定会赶上p1但是不会跳过p1的.

因为p2每次走2步,而p1走一步,所以他们之间的差距是一步一步的缩小,4,3,2,1,0 到0的时候就重合了.

根据这个方式,可以证明,p2每次走三步以上,并不总能加快检测的速度,反而有可能判别不出有环.

比如,在环的周长L是偶数的时候,初始p2和p1相差奇数的时候,p2每次走三步,就永远和p1不重合,因为他们之间的差距是:  5, 3 , 1,  
L-1, L-3

附加几点解释:虽然说p1看来“相对”于p2并不前进,但是“绝对”来说,它还是前进的,那么迟早它会跑到圆圈上,而且不再出来
4000
。p2就更是了。所以,尽管有可能p1还没有进圆圈,p2已经跑完几圈了(想象一下圆圈很小,但是前面的直道部分很长的情况),但是p1迟早要进圈,演变成跑步问题。

========================================================

下面则再进一步:如何判断链表的环开始的位置呢?也就是环与链表的交点。

这个问题就更靠近脑筋急转弯的方向了。首先想到,如果还是p1和p2一起跑圈,那仍然会发生很多次相遇,但是这个相遇跟起点并无关系,想求出入口不可能。

那我们就分析一下,p1和p2相遇的时候到底发生了什么。在示意图中,假设p1和p2在P点第一次相遇。从环路入口到P点的距离为x,从P点到环路入口的另一段的距离为y。

那么显然,第一次相遇的时候,p1走过的总长度为x+z。那p2呢?我们首先假设p2比p1只多跑了一圈(方便理解,但并不影响最后结论,下面再证明所有的情况)。那么容易得到,p2走过的总长度为z+x+y+x。这个结论很显然也是表明p2比p1多跑了一圈(一圈的长度就是x+y,与前面的分析吻合)。由于p2的速度是p1的两倍,很容易得到一个结论:z+x+y+x
= 2 * (x+z)。也就是z = y。也就是说,相遇的点离环路入口的距离与链表直道的长度一样。

3,呵呵,得到这个结论似乎就离我们的答案很近了:如何才能找到这个入口位置呢?显然,让一个指针从相遇的P点出发,另外一个指针从链表头出发,以相同的速度前进,那么它们必然会在O点也就是环路入口相遇!

补充证明:设p2跑了n圈之后,才跟p1相遇(但请注意:p1一定是在自己的第一圈,因为不可能发生两个指针都在圈内,p2超过了p1但是没相遇的情况,前面的分析里说了),那么可以得到下面的等式:

z + n * (x+y) + x = 2 * (x+z)

最后可以得到下面的式子:z = n*(x+y) - x = (n-1)*(x+y) + y。 也就是说直道的长度等于n-1圈的长度加上y的长度,仍然不影响上面的结论:如果两个指针速度相同,一个指针从head出发,另一个从P点出发(跑n-1圈,再跑y),所花时间相等。它们一定会在O点相遇!

2,对于问题2,记录下问题1的碰撞点,slow和fast从该点开始,再次碰撞所走的长度 就是换的长度。

4,链表的长度可以由2,3得到的长度和求得。

5,



将其中的一个链表首尾相连,然后判断另一个链表是否带环即可。(问题就会演变成判断是否有环了

<pre name="code" class="cpp"><pre name="code" class="cpp">#include <stdio.h>

typedef struct Node
{
int val;
Node *next;
}Node,*pNode;

//判断是否有环
bool isLoop(pNode pHead)
{
pNode fast = pHead;
pNode slow = pHead;
//如果无环,则fast先走到终点
//当链表长度为奇数时,fast->Next为空
//当链表长度为偶数时,fast为空
while( fast != NULL && fast->next != NULL)
{

fast = fast->next->next;
slow = slow->next;
//如果有环,则fast会超过slow一圈
if(fast == slow)
{
break;
}
}

if(fast == NULL || fast->next == NULL  )
return false;
else
return true;
}

//计算环的长度
int loopLength(pNode pHead)
{
if(isLoop(pHead) == false)
return 0;
pNode fast = pHead;
pNode slow = pHead;
int length = 0;
bool begin = false;
bool agian = false;
while( fast != NULL && fast->next != NULL)
{
fast = fast->next->next;
slow = slow->next;
//超两圈后停止计数,挑出循环
if(fast == slow && agian == true)
break;
//超一圈后开始计数
if(fast == slow && agian == false)
{
begin = true;
agian = true;
}

//计数
if(begin == true)
++length;

}
return length;
}

//求出环的入口点
Node* findLoopEntrance(pNode pHead)
{
pNode fast = pHead;
pNode slow = pHead;
while( fast != NULL && fast->next != NULL)
{

fast = fast->next->next;
slow = slow->next;
//如果有环,则fast会超过slow一圈
if(fast == slow)
{
break;
}
}
if(fast == NULL || fast->next == NULL)
return NULL;
slow = pHead;
while(slow != fast)
{
slow = slow->next;
fast = fast->next;
}

return slow;
}






代码二

bool IsExitsLoop(slist * head)
{
slist * slow = head ,  * fast = head;

while  ( fast  &&  fast -> next )
{
slow  =  slow -> next;
fast  =  fast -> next -> next;
if  ( slow  ==  fast )  break ;
}

return   ! (fast  ==  NULL  ||  fast -> next  ==  NULL);
}
slist *  FindLoopPort(slist * head)
{
slist * slow  =  head,  * fast  =  head;

while  ( fast  &&  fast -> next )
{
slow  =  slow -> next;
fast  =  fast -> next -> next;
if  ( slow  ==  fast )  break ;
}

if  (fast  ==  NULL  ||  fast -> next  ==  NULL)
return  NULL;

slow  =  head;
while  (slow  !=  fast)
{
slow  =  slow -> next;
fast  =  fast -> next;
}

return  slow;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐