您的位置:首页 > 其它

链表之快慢指针续

2014-10-07 16:26 246 查看
之前解决了如何判断一个单链表是否存在环的问题。

那:如果要求出环的长度呢?

如果要求出环的入口点呢?

一、求出带环单链表环的长度

求长度,就是希望有个指针能够遍历环中的每一个元素,然后记下经过的节点数就好了。

当fast==slow的时候,fast和slow已经在环中了

第一种方法:用p记录下fast所指的节点,q从这个节点开始遍历,当q再次等于p的时候,说明已经在环里走了一圈了,经过的节点数就是环的长度。

int loopLength1(Node *head)
{
    Node *slow, *fast;
    slow = fast = head;
    while(fast && fast -> next)
    {
        fast = fast -> next -> next;
        slow = slow -> next;
        if(fast == slow)
            break;
    }
    if(fast == slow)
    {
        Node *p = fast;
        Node *q = fast -> next;
        int length = 1;
        while(p != q)
        {
            q = q -> next;
            ++length;
        }
        return length;
    }
    return -1;
}


第二种方法:fast 与 slow 第一次相遇开始计数,当它们第二次相遇时,我们可以证明,它们整整走了一圈,仍然指向第一次相遇时指向的节点。为了标记是否计数以及是否是第二次相遇,就需要用到两个bool类型的变量作为标记,分别是start 和 again

证明: 若环的长度为n, 第二次相遇时slow走了x步

那么一方面fast走了2x步,另一方面fast走了x+n步。所以x等于n 即它们刚好走了一圈,回到了原点

int loopLength2(Node *head)
{
    Node *slow, *fast;
    slow = fast = head;
    bool start = false,again = false;
    int length = 0;
    while(fast && fast -> next)
    {
        fast = fast -> next -> next;
        slow = slow -> next;
        //如果再一次追赶上
        if(fast == slow && again)
            break;
        //如果第一次追赶上,则进行第二次追赶
        else if(fast == slow && !again)
        {
            start = true;
            again = true;
        }
        if (start) ++length;
    }
    if(fast == slow && again)
        return length;
    else return -1;

}

二、求出环的入口点



这里有个论断,当fast slow第一次相遇时,slow一定还没有遍历完整个链表。自己画了几次发现是这样,具体证明没找到。设a是从head到入口点的长度,x是入口点到相遇点的长度,l是链表总长度

假设slow指针走了s步,则2s = s + nr, 得出s = nr ,又s = a + x

所以a + x = nr = (n-1)r + r = (n - 1)r + l - a

即 a + x = l - a - x

a= (n - 1)r + l - a - x

l - a - x指的是从相遇点到入口点的长度

a指的是从head到入口点的长度

这个式子表明:从head出发的指针与从fast 和 slow相遇点出发的指针一定会在某个点相遇,它们相遇之处就是环的入口处。

好吧,算法:当fast和slow相遇后,令slow = head,fast不变,两者继续向后移动,知道两指针相遇,相遇之处就是环的入口处。

Node *findLoopPort(Node *head)
{
    Node *slow, *fast;
    slow = fast = head;
    while(fast && fast -> next)
    {
        fast = fast -> next -> next;
        slow = slow -> next;
        if(fast == slow)
            break;
    }
    if(fast == slow)
    {
        slow = head;
        while(fast != slow)
        {
            fast = fast -> next;
            slow = slow -> next;
        }
        return slow;
    }
    return NULL;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: