当前位置:   article > 正文

Leetcode-141.环形链表(龟兔赛跑算法)_快慢指针快指针一次移动3个结点

快慢指针快指针一次移动3个结点

在这里插入图片描述
这种可能带环的链表是比较麻烦的,要判断链表中是否有环。
我看过设置一个标记来断开一个环的算法(虽然我觉得没什么意义),两者是完全不一样的。这道题以我目前的水平不会使用一些高级的数据结构,只能通过一个算法来解答–弗洛伊德循环查找算法(龟兔赛跑算法)。
接下来,我会通过一些数学推理来验证该算法。

该算法的实现底层是快慢指针。而对于快慢指针而言,通常我们会设计快指针一次走两个结点,而慢指针一次走一个结点,这样保险一点且不会错过结点。至于为什么,我会在下面的验证过程中得出。

为什么会认为快慢指针就能得出链表中是否有环呢?

因为,如果链表中没有环时,fast会走到NULL,就可以得出链表无环,因为会结束,有NULL。
而有环时,快慢指针最终会指向同一个地址,至于为什么,请看证明。

对龟兔赛跑的推理验证

在这里插入图片描述

我们假设在线上(环外)有K+1个结点(head刚开始就是他们所在的位置)。环中有N个结点。

当fast进入结点时,走了K个结点,而slow走了K/2个结点(由于未知K的奇偶,只是初略描述,后面会更加细节)。

而当slow走到环的入口时进入环,走了K/2个结点,fast走了K个结点,在环内走了K个结点。

而此时fast距环的入口有K%N个结点。而slow是在环的入口。则这意味着两个指针相距K/N个结点。

情况1
在这里插入图片描述

情况2
在这里插入图片描述

情况3
在这里插入图片描述
对于情况3而言,当慢指针进环时,快指针已经绕环走了n圈,两者恰好走到了一起。
也可能情况1情况2都是已经走了n圈后的最后情况(千万不要被图给误导了,一切还是要看数据说话)。

这三种情况都是要考虑环外结点与环内结点的数目。

这些都是当慢指针入环的情况,还是要看看之后的步骤。
因为快指针一次走两个结点,而慢指针一次只走一个结点,所以肯定是fast去追slow。
对于上面三种情况而言,
在这里插入图片描述
在这里插入图片描述
绿色的路径才是fast要追slow的结点。而这部分节点数为N-K%N。我们设M=N-K%N。即,在刚开始时,fast追slow,要追M个结点,两者相差M个结点。两者再走一次,

fast走两个结点
slow走一个结点
两者相差M-2+1=M-1
相差M-1个结点

再走一次

ast走两个结点
slow走一个结点
两者相差M-1-2+1=M-2
相差M-2个结点

继续

相差M-3
相差M-4


这就相当于每两结点走一次,两个的距离就会缩小一个结点。
从某种意义上,相当于slow没动一直是fast一次一个结点的运动。
两指针继续走直到


相差1个结点,
相差0个结点。

两指针就走到一块了。即可证明有环。

至于为什么要一般让快指针一次走两个结点。再看。

如果快指针一次走3个结点?一次走4个结点呢?情况会怎样?

前面的步骤差不多,主要还是在环内的推理分析。
在这里插入图片描述
还是假设两指针相差M个结点。

1,相差M-3+1=M-2
2,相差M-2-3+1=M-4
3,相差M-4-3+1=M-6





C-2,相差M-2*(C-2)
C-1,相差M-2*(C-1)
走了a次后相差M-2*a个结点

就相当于,慢指针不动,快指针一次走两个结点。但是两者到底能不能指向同一个地址还要看M-2*a的值。

而,当值为0时,意味着,两指针指向同一处。但M-2*a的值还是得看M的值,如果是2的倍数才能等于0,如果不是,那么将不会存在等于0的时候,即两指针就不会指向同一个地址。

而M=2K%N(因为fast的速度是slow的3倍)。如果怀疑可能会在下一次的时候一定相遇,那我们就来判断。

则继续循环,而此时两指针相距
在这里插入图片描述

N-abs(M-2 * a)。
等于N-abs(2K%N-2 * a)。
随着循环继续,两指针差距为,{【N-abs(2K%N-2 * a)】-(2 * b)}。
假设差距能等于0,即N-abs(M-2a)为2的倍数,这全取决于.
对于M而言,诺M为奇数,则第一次是不会相遇
而第二次,取决于N-(M-2
a).
而该值是与环的节点数密切相关。是否可以快慢指针重合,全与N有关。且,第一次无法重合,即M-2*a就也会是一个奇数(不可能是偶数)。
而,诺N也是奇数,即结果就会是一个偶数,即第二次就会相遇,诺是偶数,即第二次是无法相遇。

是否相遇全取决于N与M。当快指针一次走3步,或4步的时候。就容易出现无法重合的问题。

所以在大多数情况下,快指针一般是走两步,比慢指针快一个结点。

龟兔赛跑算法实现

针对题目的算法实现

bool hasCycle(struct ListNode *head) 
{
    struct ListNode* fast=head,*slow=head;
    int i=0;
    while(fast&&fast->next)
    {
        
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
        {
            return 1;
        }
    }
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

快慢指针,依次循环遍历。
如果fast找到NULL就意味着无环结构。
否则就继续循环遍历,直到快慢指针指向同一地址,就结束函数,同时返回1,代表链表中有环结构。
在循环条件中,设置一个fast->next,是为了防止指针越界。毕竟快指针要走两个结点。

感谢观看,我知道在上面推理上数学验证是不严谨的,由于我目前数学水平不够。日后会对该算法有完整的推理。
谢谢大佬观看

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/64485
推荐阅读
相关标签
  

闽ICP备14008679号