寻找带环的链表的柄长

试题:
给定一个带环的链表,找出环起点。
比如:A -> B -> C -> D -> E -> C (C为环形起点)
写一个程序找出环起点C。

ListNode结构如下,请实现 ListNode* find_circle_beginning(ListNode* head);函数,返回环的起点。

struct ListNode
{
    char val;
    ListNode* next;
};

 

答案:

1、先用快指针(每次走两步)和慢指针(每次走一步),遍历链表,当两个指针相遇时,说明该链表存在环。
2、当两个指针遇到时,慢指针退回到链表起点,快指针保留在相遇点,两个指针都以一次一步前进,再次相遇的点就是环的起点。

 

源码如下:

ListNode* find_circle_beginning(ListNode* head)
{
    if( head == NULL )
    {
        return NULL;
    }

    ListNode* slow = head;
    ListNode* fast = head;
   
    // 寻找相遇点
    while( slow != NULL && fast != NULL && fast->next != NULL )
    {
        slow = slow->next;
        fast = fast->next->next;
        if( fast == slow )
        {
            // 在此相遇,说明存在环
            break;
        }
    }

    // 如果两个指针不等,说明不存在环
    if( fast != slow )
    {
        return NULL;
    }

    /*
        慢指针退回到链表起点,快指针保留在相遇点,两个指针都以一次一步前进,再次相遇的点就是环的起点。
        稍后会给出证明
    */  
    slow = head;
    while( slow != fast )
    {
        slow = slow->next;
        fast = fast->next;
    }

    return fast;
}

 

证明:

假设环柄长度AB为h,环的长度为p,相遇点为C.

 

当快指针和慢指针相遇时,慢指针在环上走了m步,总共就是h+m,快指针走过的长度就是2(h+m),在环上走过的长度就是h+2m。在相遇点相遇时,存在以下关系:
(h+2m)%p = m%p
==> ((h+m)+m)%p = m%p
==> (h+m)%p = 0
==> h%p + m%p = p      式(1)

 

假设BC的长度是m1,则有m%p = m1,带入式(1)得到:
h%p = p - m1
==> h = (p - m1) + ap (a >= 0)

当第二次相遇时,慢指针从头走过h,快指针走(p-m1)+ap,然后在环起点相遇。

 

小结:

本文的证明实际是倒推的结果。该方法实际上是叫做Pollard's Rho Method,详见http://www.csh.rit.edu/~pat/math/quickies/rho/

 

posted @ 2014-06-27 17:47  shokey520  阅读(238)  评论(0编辑  收藏  举报