判断单链表是否存在环及寻找环的入口点

判断一个单链表是否存在环的解法如下:

1.遍历链表,将链表的节点存入hash表内,若遍历到后面的节点已存在hash表内,说明有环。时间:O(n) 空间:O(n)

2.反转链表,时间:O(n) 空间:O(1),使用3个指针:pNext、pPrev、pCur。这种方法有副作用,就是若存在环的话,无法还原到链表原始状态。(弃用)

3.快慢指针法,时间:O(n) 空间:O(1)。

问题1:快慢指针何时相遇,是否回转几十圈,才相遇呢?

证明1:设环长为L,slow指针第一次进入环内,fast指针在前方的a节点处(0<a<L),经过x次移动后两个指针相遇。那么即有:

0+x = (a+2x)(mod L) => 0= a+x(mod L) 两边同时减去x

就是说a+x对L求余应该是0,即a+x=0,L,2L……这一系列的解取最小的那个。

得出x=L-a,就是说slow指针进入环后向前移动L-a个位,fast指针向前移动了2L-2a个位,如下图所示:

绘图1

问题2:如何求前面的子链长度呢?(即环入口点)

证明2:假设在fast与slow第一次相遇是,slow总共走了n步,fast走了2n步。

slow的路径为:n=p+x,x为相遇点到环入口的距离。

fast的路径为:2n=p+x+k*L

可以看出slow到达相遇点后再走n步,还是回到相遇点(p+x)。

将fast置到起点,步长为1,经过n步,也会到达相遇点。这个过程中只有前p步的路径不同,所以当slow与fast再次重合是必然在环入口点上。

绘图2

下面附上代码:

#include <iostream>
using namespace std;

struct node
{
    int value;
    node* next;
    node(node *p=NULL)
    {
        value=0;
        next=p;
    }
    node(const int v,node *p=NULL)
    {
        value=v;
        next=p;
    }
};

node* detectLinkListLoop(node *first)
{
    node *fast,*slow;
    fast=slow=first;

    while(fast && fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
        if (fast == slow)
            break;
    }
    return !(fast == NULL || fast->next == NULL);
}

node* FindLoopPort(node *head)
{
    node *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;
}
void main()
{
    node* A=new node(1);
    node* B=new node(2);
    node* C=new node(3);
    node* D=new node(4);
    node* E=new node(5);
    node* F=new node(6);
    node* G=new node(7);
    node* H=new node(8);
    A->next=B;
    B->next=C;
    C->next=D;
    D->next=E;
    E->next=F;
    F->next=G;
    G->next=H;
    H->next=C;

    node* first=A;

    cout<<detectLinkListLoop(A)<<endl;
    cout<<FindLoopPort(A)<<endl;
}

感谢两位gocalf、xudacheng06博主的指导,参考文章:

http://blog.csdn.net/xudacheng06/article/details/7706245

http://www.gocalf.com/blog/circle-of-link-list.html

posted @ 2013-10-07 12:14  Linka  阅读(329)  评论(0编辑  收藏  举报