判断单链表是否有环(转载)

2011-10-30 11:42:14|  分类: 默认分类|字号 订阅

 
 

问题:
1、如何判断一个单链表是否有环?
2、如果链表为存在环,如果找到环的入口点

问题1解法:

设置两个指针(fast, slow),初始值都指向头,slow每次前进一步,fast每次前进二步,如果链表存在环,则fast必定先进入环,而slow后进入环,两个指针必定相遇。(当然,fast先行头到尾部为NULL,则为无环链表)程序如下:

View Code
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);
}

证明:

2011年10月30日 - 东方雨中漫步者 - 东方雨中漫步者

 slow首次在A点进入环路时,fast一定在环中的B点某处。设此时slow距head长为x,B点距A点长度为y,环周长为s。因为fast和slow的步差为1,所以slow前行距离为y的时候,恰好会被fast在M点追上。因为y<s,所以slow尚未完成一次遍历。

问题2解法:

View Code
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;
}

证明:

当fast按照每次2步,slow每次一步的方式走,发现fast和slow重合,确定了单向链表有环路了

接下来,让fast回到链表的头部,重新走,每次步长不是走2了,而是走1,那么当fast和slow再次相遇的时候,就是环路的入口了。

这点可以证明的:

在fast和slow第一次相遇的时候,假定slow走了n步骤,环路的入口是在p步的时候经过的,那么有

slow走的路径: p+c = n;         c为fast和slow相交点,距离环路入口的距离

fast走的路径: p+c+k*L = 2*n;   L为环路的周长,k是整数

显然,如果从p+c点开始,slow再走n步骤的话,还可以回到p+c这个点

同时fast从头开始走的话,经过n步,也会达到p+c这点

显然在这个步骤当中fast只有前p步骤走的路径不同,所以当slow和fast再次重合的时候,必然是在链表的环路入口点上。

扩展问题:
判断两个单链表是否相交,如果相交,给出相交的第一个点(两个链表都不存在环)。
比较好的方法有两个:
一、将其中一个链表首尾相连,检测另外一个链表是否存在环,如果存在,则两个链表相交,而检测出来的依赖环入口即为相交的第一个点。
二、如果两个链表相交,那个两个链表从相交点到链表结束都是相同的节点,我们可以先遍历一个链表,直到尾部,再遍历另外一个链表,如果也可以走到同样的结尾点,则两个链表相交。

posted @ 2012-08-14 15:15  爱也玲珑  阅读(1234)  评论(0编辑  收藏  举报