寻找带环的链表的柄长
试题:
给定一个带环的链表,找出环起点。
比如: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/
It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity. We had everything before us, we had nothing before us.
这是最好的时代,这是最坏的时代;这是智慧的年代,这是愚蠢的年代;这是信仰的时期,这是怀疑的时期;我们的前途拥有一切,我们的前途一无所有。
这是最好的时代,这是最坏的时代;这是智慧的年代,这是愚蠢的年代;这是信仰的时期,这是怀疑的时期;我们的前途拥有一切,我们的前途一无所有。