zoukankan      html  css  js  c++  java
  • 追赶法检测链表环点的算法正确性分析

    C专家编程在附录A2讨论了链表环检测的算法,实现一下,算法一通过设置标志,复杂度O(N),算法二就是所谓的追赶法,算法还是很好理解的,在链表尾部形成的环中,假设两个步伐不一致的的人在绕圈,总有一个交点,但是要证明该算法的正确性似乎还要费一些精力。

    先给出实现:

       1:  /*
       2:  Expert C Programming,Chinese Edition
       3:  How to detect loop in linked list, p274,solution1
       4:  */
       5:  int check_loop1(pnode list)
       6:  {
       7:      pnode p=list;
       8:      while(p!=NULL)
       9:      {
      10:          if(p->flag==1)
      11:          {
      12:              return 1;
      13:          }
      14:          else
      15:          {
      16:              p->flag=1;
      17:          }
      18:          p=p->next;
      19:      }
      20:   
      21:      return 0;
      22:  }
      23:   
      24:  /*
      25:  Expert C Programming,Chinese Edition
      26:  How to detect loop in linked list, p274,solution4
      27:  */
      28:  int check_loop2(pnode list)
      29:  {
      30:      pnode p1=list;
      31:      pnode p2=list;
      32:      if(p1==p2->next)
      33:      {
      34:          return 1;
      35:      }
      36:   
      37:      while(p1->next!=NULL && p2->next!=NULL)
      38:      {
      39:          p1=p1->next;
      40:          p2=p2->next->next;
      41:          if(p1==p2)
      42:          {
      43:              printf("%d",p1->value);
      44:             return 1;
      45:          }
      46:      }
      47:   
      48:      return 0;
      49:  }

    image

    设链表长度为n,出现环点处为a.

    要证明在有环链表中,步长为1和2的两个向量移动必有相遇点,也就意味着要解方程。

    向量1移动k步之后所在的点应该是:

    image

    向量2移动k步之后所在的点应该是:

    image

    其中的除法运算应视作求模运算。

    显然解方程就是求存在这样的k使得两个向量组有共解。比如如果k=0,则k=2k,意味着在链表第一个节点就开始循环。

    如果循环点就是链表尾端,则意味着n=a,于是(2k-a) mod (n-a+1)永远为0,=>a=k,意味着a步之后就能侦测出现环。

    如果是其它情况,就意味着要解方程:

    k=(2k-a) mod (n-a+1)+a                       (1)

    或者另外一个

    (k-a) mod (n-a+1) +a=(2k-a) mod (n-a+1)+a微笑,     (2)

    要证明对于任意的n与a,都有整数k存在使得等式成立。

    ---------------------------------------------

    现观察方程(2),等式两边消去a后这是一个典型的同余方程,我们根据同余的定义(这里我参考了Rosen的《离散数学及其应用》中文版p152),要使方程成立,则有且仅有(n-a+1)|(2k-a-k+a),即n-a+1整除k,于是我们知道k的值就是d*(n-a+1),其中d为正整数,且k>=a(这是很明显的,否则步长为1的向量还没走到环点,也意味还没进环呢热烈的笑脸),于是我们知道方程(2)必然有解。分析完毕。

    再注:第一个向量组中的两个表达式其实都可以用(k-a) mod (n-a+1)+a表示。

    复杂度分析:

    已知k的形式如:k=d*(n-a+1)且k>=a,故两向量第一次相遇时的步长k就取第一个大于a的d*(n-a+1),其步长是线性的,故复杂度可以表示为O(N).

     

    还能再精确一点嘛?能否确定第一个k不小于链表长度呢?因为对比我简单的草稿,所有的结果都暗示可能有这个结论。

    如果是死理性派在这里,他们肯定会说这是应该的,假设他们就在这里,现猜测:第一个k应该<=n,为证明猜想,现需要求解不等式n>=d*(n-a+1)>=a即:n+1>d*(n-a+1)>a-1

    即:(n+1)/(n-a+1)>d>(a-1)/(n-a+1)

    于是需要证明存在这样的正整数d。而域『(a-1)/(n-a+1),(n+1)/(n-a+1)』的长度为(n-a+2)/(n-a+1)显然长度大于1,

    于是可知必然存在满足不等式的正整数d。于是

    我们就此得知,在n步之内,必然两向量相遇。也意味着步长为1的向量顶多走n步,而别忘了,n是单链表的长度(节点数)。

    于是找到环所需要的时间复杂度就是O(N),也即是O(1).

    ------------------------------------------------------------

  • 相关阅读:
    公司到底是怎么看我们的……[转]
    C# String.Format 的使用[转]
    Oracle9I 在安装时出现[登台区出现问题,请确保指定有效的“源”和“目标”!]
    街机游戏下载
    C#操作Excel时直接引用Com和InteropExcel的差异
    我所理解的接口和抽象类[转]
    c# winFrom 使窗体显示SplitContainer或Panel中[转]
    SQL Server 2005之PIVOT/UNPIVOT行列转换(转)
    Solaris大半年使用感触
    solaris上的pkg管理
  • 原文地址:https://www.cnblogs.com/dancewithautomation/p/2578547.html
Copyright © 2011-2022 走看看