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

【链表】

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值