弗洛伊德的乌龟和兔子

一、问题来源 : LeetCode287. Find the Duplicate Number

 

二、解决问题 :含环链表中环入口结点的寻找问题

 

三、算法介绍

1、传统方法:用HashSet来解决

假设ListNode类已定义,则按序存入Set后第一个重复的结点即为环入口

public ListNode EntryNodeOfLoop(ListNode pHead){
    HashSet<ListNode> hs = new HashSet<ListNode>();
        while(pHead!=null){
            if(!hs.add(pHead))//如果包含了,那么这个就是入口结点
                 return pHead;
            pHead = pHead.next;
         }
        return null;
 }
 
 如果采用这种方法,必须完整得完成一次环结点的遍历;
 
 2、Floyd算法(弗洛伊德的乌龟和兔子问题)
 
 【1】算法介绍
 
 这里的思路本质上是快慢指针,hare(兔指针,代表快结点)每次走2步,tortoise(龟指针,代表慢节点)每次走一步,令x是出发点到环的起点的距离,y是环的长度,可以证明,乌龟最多走x+y步,兔子最多2(x+y)步。证明如下:
 
 假设乌龟已经进入环中并已经移动了y1步,则此时兔子已经移动了2*(x+y1)步,如果要两者相遇,则需要步数相差一个环的长度,即x+y1 = y...(1),由此可以得出,y1 <= y,当仅当x == 0时等号成立。
 
 但是到目前为止,只是能够判断链表中是否有环,相遇的位置只能判断是在环内而大概率不是环的起点,如果需要找到环的起点还得继续计算。
 
 我们可以从(1)式得到y1 = y-x,所以环还剩x步需要走,故将hare移动到起始点,双指针均每次走一步,x步后两个指针将在环的起始点相遇。
 
 【2】算法比较
 
 相较于使用HashSet,Floyd算法减少了额外空间的使用(o(n) -> o(1)),但是同时没有增加时间复杂度(从tortoise的移动可以看出,Floyd算法依旧完整的走完了x+y)。

 所以如果只需要判断是否存在环,Floyd方法更有优势。
 
 
 3、Brent算法
 
 Brent算法实际上是对Floyd算法的判断是否存在环的环节进行了优化。对于第i轮移动(轮数从0开始计算),乌龟每次移动i步,兔子每次移动2^i步。可以证明乌龟在环内还是最多走一圈。证明如下:
 
 在n轮结束时,兔子共走了2^0+2^1+...+2^(n-1) = 2^n-1步,而第n+1轮兔子需要走2^n步,所以兔子每一步需要走出(之前的总步数+1)步。设n轮时乌龟在环内且位于y1的位置,即乌龟移动的距离为x+y1,则下一步兔子将一共走出(x+y1)+(x+y1)+1 = 2*(x+y1)+1的距离,当两者相遇时,有乌龟位于y2位置,此时有:

x + y2 = 2*(x + y2) + 1 = x + y2 + t*y...(2), 即t*y = x + y2 + 1。
 
 【2】算法比较
 
 相较于Floyd算法,时间优化接近30%
 

四、参考来源

1、Title:一个链表中包含环,请找出该链表的环的入口结点  Website:https://www.cnblogs.com/fankongkong/p/7007869.html

2、Title:弗洛伊德的乌龟和兔子--leecode刷题  Website:https://blog.csdn.net/cirol1997/article/details/103453514

3、Title:brent算法为什么比floyd算法快 Website:https://blog.csdn.net/qq_35688140/article/details/86586035

4、Title:Brent判圈算法学习 Website:http://zhengyhn.github.io/post/algorithm/brent.loop/
 

发布了39 篇原创文章 · 获赞 2 · 访问量 1750
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览