【数据结构和算法】证明链表有环的算法正确性
转载至博客:https://blog.csdn.net/mucaoyx/article/details/81395782
双指针判断单链表是否有环的正确性证明
问题:给你一个单链表,需要找到一个方法进行判断是否有环的存在。
这篇文章主要证明一下,为什么存在环的情况下两个指针(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)
下面进行验证一下。对于图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)
m满足:
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
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)
下面进行化简与推导:
∵ m=n+1−k%(n+1)
∴ k+(x+2∗m)%(n+1)=k+m%(n+1)
∵ m<(n+1)
∴ m%(n+1)=m
∴ k+(x+2*m)%(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指针相遇。
===============证明思路==============
1、先通过一个例子进行说明思路。
2、将例子进行泛化,用参数表现。
3、分析问题,转化成数学的追及问题。
4、求证,当slow进入环后,fast与其的相对位置,以及fast和slow相遇的条件。证明两者移动后的位置,是否相等。
===============链表有环的算法========
package com.spring.test.service.algorithm.problem; /** * @date 8:22 PM 2019/6/16 */ public class LinkedListHasARing { /** * 判断Node是否有环 * * @param node * @return */ public static boolean isHasARing(Node node) { if (node == null) { return false; } Node slow = node; Node fast = node; while (fast != null && fast.next != null) { //循环不变式,如果最快的节点不为空,且它的下一个节点也不为空,则说明链表还没有到尾端 //移动快的节点,如果到尾端了,还没有跳出循环,说明无环 //slow移动一个节点 slow = slow.next; //fast移动两个节点 fast = fast.next.next; if (slow == fast) { //如果fast节点等于next节点,则证明链表有环 return true; } } return false; } } /** * 链表节点 */ class Node { String obj; Node next; }