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
    评论
Floyd判圈算法,又称龟兔赛跑算法,是用来判断链表是否有环、计算环的长度以及寻找环的起点的一种算法。该算法通过使用两个指针,一个慢指针和一个快指针,在链表中进行遍历。慢指针每次移动一步,而快指针每次移动两步。如果链表中存在环,那么快指针最终会追上慢指针,两个指针会相遇。这可以通过两个条件来判断:当快指针和慢指针相遇时,且快指针不为null且快指针的下一个节点也不为null。如果两个指针相遇,那么说明链表中存在环。接下来,我们可以从相遇点开始,用一个指针再次遍历链表,直到回到相遇点,记录遍历过程中的节点数。这个节点数就是环的长度。 如果慢指针和快指针相遇,那么我们可以进一步判断是否有环存在。如果相遇点是链表中的某个节点,那么从相遇点开始遍历链表,再次回到相遇点的位置一定是环的起点。这是因为从相遇点继续遍历,最终会再次回到相遇点,而在这个过程中,慢指针每次移动一步,快指针每次移动两步,所以在某一时刻,慢指针和快指针会在环的起点处相遇。 然而,如果慢指针和快指针从环的不同起点出发,那么它们再次相遇的位置会发生偏移,不会是环的起点。同样地,如果慢指针和快指针从环外的同一起点出发,它们在进入环之前还有一段距离,因此再次相遇的位置也不一定是环的起点。 综上所述,Floyd判圈算法通过两个指针的移动,可以判断链表是否有环,并且计算出环的长度和寻找环的起点。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Floyd判圈算法](https://blog.csdn.net/qq_26012495/article/details/117407011)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [cjing-interview:C ++面试; 基本算法数据结构提示](https://download.csdn.net/download/weixin_42144707/16474761)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值