如何判断单链表有环及正确性证明

双指针判断单链表是否有环的正确性证明

​问题:给你一个单链表,需要找到一个方法进行判断是否有环的存在。
这篇文章主要证明一下,为什么存在环的情况下两个指针(slow和fast指针)就一定会相遇。

双指针判断单链表是否有环

​使用两个slow, fast指针从头开始扫描链表。指针slow 每次走1步,指针fast每次走2步。如果存在环,则指针slow、fast会相遇;如果不存在环,指针fast遇到NULL退出。

其实就是所谓的追及相遇问题:

这里写图片描述

证明存在环就会导致两个指针(slow和fast指针)一定相遇。

现在,我把情况还原到完整的环中,看看具体发生的情况到底是什么?
首先看下面的图,图中的单链表是有环的。
在这里插入图片描述

图1. 有环的单链表

在这里插入图片描述

图2. 步骤一

在这里插入图片描述

图3. 步骤二

在这里插入图片描述

图4. 步骤三

从图4可以看出,经过两次移动,fast指针刚好进入环,指向node4节点,而slow指针才移动到node2节点。

在这里插入图片描述

图5. 步骤四

在这里插入图片描述

图6. 步骤五
从图6可以看到,当slow指针移动到node4节点,即刚进入环时,fast指针已经移动到node8节点了。

在这里插入图片描述

图7. 步骤六

从图7中看到,非常的巧,slow指针进入环后,向前移动了一次就和fast指针相遇了。

将上述的过程一般化。
首先可以肯定得是fast指针一定是先进入到环中的,随后slow指针才会进入到环中,可能slow进入环中时,fast指针已经在环里面循环很多圈了,这都没关系,我们只关注slow指针进入环时,fast指针与slow指针的相对位置。
在这里插入图片描述

图8. 泛化

链表除头节点外,所有的节点都统一编号,进入环的节点是node k,图中只是标注了编号。整个环由k节点,k+1节点,…k+n节点构成,一共是(n+1)个节点。
最开始的时候slow指针和fast指针都指向头节点,然后两个指针同时移动k次:
(1) slow指针每次移动一个节点,到达k号节点,刚好是进入环的节点。
(2) fast节点每次移动二个节点,通过2*k个节点,到达k+x号节点,处于环中。
这个x可以计算出来,如下所示:

x = ( 2 ∗ k − k ) % ( n + 1 ) x =(2*k -k)\%(n+1) x=2kk%n+1

下面进行验证一下。对于图6而言,k=4,n=4,所以x=4,所以当slow指向node4时,fast指针应该指向node(k+x)就是node8,可以看到图6已经验证了这个结论。

下面计算一下slow指向k节点时,fast与slow地相对距离m。

m = n − x + 1 = n + 1 − ( 2 ∗ k − k ) % ( n + 1 ) = n + 1 − k % ( n + 1 ) \begin{aligned} m=&n-x+1\\ =&n+1-(2*k -k)\%(n+1) \\ =&n+1 - k\%(n+1) \end{aligned} m===nx+1n+1(2kk)%(n+1)n+1k%(n+1)

m满足:

m &lt; ( n + 1 ) m&lt;(n+1) m<(n+1)

也就是说保持slow指针不动,fast指针再向前走m个节点就可以和slow指针相遇。
由于slow指针向前移动一个节点,fast指针就会向前移动两个节点,所以同时再移动m次就可以相遇了。下面来给出证明。
由上面的假设可知,当slow进入环时,即指向node k节点时,fast指针与slow指针的相对距离是m,也就是此时保持slow不动,fast再向前移动m个节点就可以和slow相遇。
那么,现在slow和fast同时移动m次。slow节点会到达的节点为:
k + m k+m k+m
fast节点会到达:

k + ( x + 2 ∗ m ) % ( n + 1 ) = k + ( ( 2 ∗ k − k ) % ( n + 1 ) + 2 ∗ m ) % ( n + 1 ) = k + ( k % ( n + 1 ) + 2 ∗ ( ( n + 1 ) − k % ( n + 1 ) ) ) % ( n + 1 ) = k + ( k % ( n + 1 ) + 2 ∗ ( n + 1 ) − 2 ∗ k % ( n + 1 ) ) % ( n + 1 ) = k + ( 2 ∗ ( n + 1 ) − k % ( n + 1 ) ) % ( n + 1 ) = k + ( n + 1 − k % ( n + 1 ) ) % ( n + 1 ) + ( n + 1 ) % ( n + 1 ) = k + ( n + 1 − k % ( n + 1 ) ) % ( n + 1 ) \begin{aligned} k+(x+2*m)\%(n+1)=&amp;k+((2*k -k )\%(n+1) + 2*m)\%(n+1) \\ =&amp;k+(k\%(n+1)+ 2*((n+1) - k\%(n+1)))\%(n+1) \\ =&amp;k+(k\%(n+1) + 2*(n+1) - 2*k\%(n+1))\%(n+1) \\ =&amp;k+(2*(n+1)-k\%(n+1))\%(n+1) \\ =&amp;k+(n+1 - k\%(n+1))\%(n+1)+(n+1)\%(n+1) \\ =&amp;k+(n+1 - k\%(n+1))\%(n+1) \end{aligned} k+(x+2m)%(n+1)======k+((2kk)%(n+1)+2m)%(n+1)k+(k%(n+1)+2((n+1)k%(n+1)))%(n+1)k+(k%(n+1)+2(n+1)2k%(n+1))%(n+1)k+(2(n+1)k%(n+1))%(n+1)k+(n+1k%(n+1))%(n+1)+(n+1)%(n+1)k+(n+1k%(n+1))%(n+1)
下面进行化简与推导:
∵ m = n + 1 − k % ( n + 1 ) ∴ k + ( x + 2 ∗ m ) % ( n + 1 ) = k + m % ( n + 1 ) ∵ m &lt; ( n + 1 ) ∴ m % ( n + 1 ) = m ∴ k + ( x + 2 ∗ m ) % ( n + 1 ) = k + m \begin{aligned} \because \quad &amp;m=n+1-k\%(n+1) \\ \therefore \quad &amp;k+(x+2*m)\%(n+1)=k+m\%(n+1) \\ \because \quad &amp;m&lt;(n+1) \\ \therefore \quad &amp;m\%(n+1)=m \\ \therefore \quad &amp;k+(x+2*m)\%(n+1)=k+m \end{aligned} m=n+1k%(n+1)k+(x+2m)%(n+1)=k+m%(n+1)m<(n+1)m%(n+1)=mk+(x+2m)%(n+1)=k+m

所以slow指针进入到环时,fast和slow指针共同再移动m次就可以在k+m节点相遇了。

综上所述:

slow指针进入环后,只要再走m步(fast落后slow指针的节点数)就一定会和fast指针相遇。slow指针进入环后,假设fast指针再往前走m个节点就会到达当前slow指针的位置,那么当slow指针和fast指针同时移动的时候,slow指针每向前移动一个节点,fast指针就会向前移动两个节点,fast与slow之间的距离就会缩短一个,所以当移动m步之后,fast指针就会和slow指针相遇。

如果证明有误,欢迎留言指正~

参考资料:检测单链表是否有环

  • 14
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值