2-5 求有环链表的环入口节点(证明及代码)

来源:互联网 发布:机柜网络模块接线图 编辑:程序博客网 时间:2024/05/01 15:38

【链表】

Q:Given a circular linked list, implement an algorithm which returns node at the begin-ning of the loop  
DEFINITION
Circular linked list: A (corrupt) linked list in which a node’s next pointer points to an 
earlier node, so as to make a loop in the linked list  
EXAMPLE
Input: A -> B -> C -> D -> E -> C [the same C as earlier]
Output: C

题目:写一个算法求出有环单链表中环的起始点。

举例:

输入:A -> B -> C -> D -> E -> C

输出:C(环的入口节点)

解答:


针对这个问题,一个前置问题是:判断一个链表是否有环?这个问题有快慢指针法、反转链表法、hash法等。

如果我们已知一个链表有环结构存在,我们如何找出环的入口节点呢?


同样可以使用快慢指针的方法,快指针fast每次走2步,慢指针slow每次走一步,当fast和slow相遇时,我们确定了单向链表有环路。接下来,让fast回到链表的头部,重新走,但这次让fast和slow以同样的速度走,每次走1步。那么当fast和slow再次相遇的时候,就是环路的入口了。为什么???


证明:在fast和slow第一次相遇的时,假定slow走了n步,环路的入口是在p步,那么:

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

           fast走的路径: p+c+k*L = 2*n; L为环路的周长,k是整数。因为fast比slow多走的路就是它多绕了几个圈。

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

          同时,fast从头开始走,步长为1,经过n步,也会达到p+c这点。

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


有了思路以后,我们可以编码如下(C代码):
NODE* find_loop(NODE* list){     if(list==NULL)        return NULL;    NODE *fast=list,*slow=list;    while(fast->next!=NULL){        fast=fast->next->next;        slow=slow->next;        if(fast==slow)            break;        }    if(fast->next==NULL)//如果fast走到尽头了,说明没有环结构存在        return NULL;    slow=list;    while(fast!=slow){//再次相遇时,在环入口处        fast=fast->next;        slow=slow->next;        }    return fast;}

JAVA参考代码:

LinkedListNode FindBeginning(LinkedListNode head) {    LinkedListNode n1 = head;    LinkedListNode n2 = head;         // Find meeting point    while (n2.next != null) {       n1 = n1.next;       n2 = n2.next.next;       if (n1 == n2) {         break;       }    }     // Error check - there is no meeting point, and therefore no loop    if (n2.next == null) {      return null;    }     /* Move n1 to Head. Keep n2 at Meeting Point.  Each are k steps    /* from the Loop Start. If they move at the same pace, they must     * meet at Loop Start. */    n1 = head;     while (n1 != n2) {       n1 = n1.next;       n2 = n2.next;     }    // Now n2 points to the start of the loop.    return n2;  }

作者:Viidiot  微信公众号:linux-code


原创粉丝点击