追赶法检测链表环点的算法正确性分析

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).

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

原文地址:https://www.cnblogs.com/dancewithautomation/p/2578547.html