赞
踩
这种可能带环的链表是比较麻烦的,要判断链表中是否有环。
我看过设置一个标记来断开一个环的算法(虽然我觉得没什么意义),两者是完全不一样的。这道题以我目前的水平不会使用一些高级的数据结构,只能通过一个算法来解答–弗洛伊德循环查找算法(龟兔赛跑算法)。
接下来,我会通过一些数学推理来验证该算法。
该算法的实现底层是快慢指针。而对于快慢指针而言,通常我们会设计快指针一次走两个结点,而慢指针一次走一个结点,这样保险一点且不会错过结点。至于为什么,我会在下面的验证过程中得出。
为什么会认为快慢指针就能得出链表中是否有环呢?
因为,如果链表中没有环时,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-2a).
而该值是与环的节点数密切相关。是否可以快慢指针重合,全与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; }
快慢指针,依次循环遍历。
如果fast找到NULL就意味着无环结构。
否则就继续循环遍历,直到快慢指针指向同一地址,就结束函数,同时返回1,代表链表中有环结构。
在循环条件中,设置一个fast->next,是为了防止指针越界。毕竟快指针要走两个结点。
感谢观看,我知道在上面推理上数学验证是不严谨的,由于我目前数学水平不够。日后会对该算法有完整的推理。
谢谢大佬观看
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。