Floyd判圈算法(龟兔赛跑算法)

问题:如何检测一个链表是否有环呢?如果有,那么怎么确定环的起点?如何确定环的长度?

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
    好比两个人在赛跑,A速度快,B速度慢,经过一定时间后,A总会和B相遇,且相遇时,A跑过的总距离减B跑过的总距离一定是圈长的n倍。

Floyd使用两个指针,一个慢(龟)指针每次前进一步,一个快指针(兔)每次前进两步(两步或多步效果是等价的,只要一个比另一个快就行。但是随着移动步数的增加,算法的复杂度可能增加)。如果两者在链表头以外(不包含开始情况)的某一点相遇(即相等),那么说明这个链表就是有环的,否则,如果(快慢指针)到达了链表尾部(果然存在结尾,肯定无环),那么无环。

假设一个链表是下面这样一个:

在这里插入图片描述
设环长为L,非环形部分长度为m,相遇处离环入口为距离k,当第一次相遇时,显然慢指针slow可能已经走了m+aL+k(a=0,1,2,…),a为慢指针走过的环圈数(如果当环很大的时候,可能好几圈才能相遇),然而快指针fast走了m+bL+k的距离。

一开始已经说明快指针fast的速度为慢指针slow的两倍,所以,上述的距离还存在这样的关系。

S fast=2S slow
代入到上述关系可以得出
m+k=(a-2b)L (a>2b)
不妨设a-2b=K,将上式改写为
m=KL-k
m=(K-1)L+L-k
设 L-k为c
m=(K-1)L+c
其实这里的距离c就是相遇点继续走到环入口的距离,这里其实就可以看出这个c其实m是相等,因为不妨大胆假设这个K=1,那么上式不就是m=c。到这里,一切都真相大白了。

在这里插入图片描述接着将慢指针重新放置到起点出发,快指针从相遇点出发,如上图,让快慢指针每次都走一步,它们再次相遇的点就是环的入口。

代码实现:

class  ListNode{
    public int data;
    ListNode next;
}
public class TestSolution {
    ListNode listNode=new ListNode();
    public ListNode findBeginLoop(ListNode head){
        ListNode slowPtr=head;
        ListNode fastPtr=head;
        boolean loopExists=false;
        if(head==null){
            return head;
        }
        while(slowPtr.next!=null&&fastPtr.next.next!=null){
            slowPtr=slowPtr.next;
            fastPtr=slowPtr.next.next;
            if(slowPtr==fastPtr){
                loopExists=true;
                break;
            }
        }
        if(loopExists){//环存在
            slowPtr=head;
            while(slowPtr!=fastPtr){
                slowPtr=slowPtr.next;
                fastPtr=fastPtr.next;
            }
            return slowPtr;
        }
        return null;//环不存在
    }
}

如果需要求环的长度,只需要固定一个指针,让另一个指针继续在环中行走,同时计数count,再次相遇count即为环长!

文章内容主要参考了以下博客,做了少许改动,感谢前辈们的付出和指导!

https://blog.csdn.net/u012534831/article/details/74231581
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值