Floyd 判圈算法(Floyd Cycle Detection Algorithm),又称龟兔赛跑算法(Tortoise and Hare Algorithm),是一个可以在有限状态机、迭代函数或者链表上判断是否存在环,以及判断环的起点与长度的算法。

结论

  • 1、如果链表上存在环,那么在某个环上以不同速度前进的2个指针必定会在某个时刻相遇;
  • 2、根据结论1找到的相遇点可找到环的入口,初始化额外的两个指针: ptr1 ,指向链表的头, ptr2 指向相遇点。然后,每次将它们往前移动一步,直到它们相遇,它们相遇的点就是环的入口。

结论1是很显然的,结论2似乎有点匪夷所思,下面将针对以上结论分别进行证明。

证明

1.龟兔相遇

一个跑得快的人和一个跑得慢的人在一个圆形的赛道上赛跑,会发生什么?在某一个时刻,跑得快的人一定会从后面赶上跑得慢的人。

下图说明了这个算法的工作方式。

初始状态下,假设已知某个起点节点为节点F。现设两个指针 fastslow,将它们均指向F。

同时让 fastslow 往前推进,fast的速度为 slow 的2倍),直到 fast 无法前进,即到达某个没有后继的节点时,就可以确定从F出发不会遇到环。反之当 fastslow 再次相遇时,就可以确定从F出发一定会进入某个环,设其为环C( fastslow 推进的步数差是环长的倍数)。

2.计算环的入口

如何找到环的入口?

根据结论1找到的相遇点可找到环的入口,初始化额外的两个指针: ptr1 ,指向链表的头, ptr2 指向相遇点。然后,每次将它们往前移动一步,直到它们相遇,它们相遇的点就是环的入口。

下图对结论2进行证明。

我们利用已知的条件:慢指针移动 1 步,快指针移动 2 步,来说明它们相遇在环的入口处:(下面证明中的 tortoise 表示慢指针,hare 表示快指针)

因为 F = b ,指针从 h 点出发和从链表的头出发,最后会遍历相同数目的节点后在环的入口处相遇。

算法描述

public class Solution {private ListNode getIntersect(ListNode head) {ListNode tortoise = head;ListNode hare = head;while (hare != null && hare.next != null) {tortoise = tortoise.next;hare = hare.next.next;if (tortoise == hare) {return tortoise;}}return null;
}public ListNode detectCycle(ListNode head) {if (head == null) {return null;}// 通过结论1,找到相遇点ListNode intersect = getIntersect(head);// 相遇点为空,则链表为非循环链表if (intersect == null) {return null;}// 通过结论2,找到环的入口// 分别定义两个指针,从head和相遇点开始前进,相遇点即为环的入口ListNode ptr1 = head;ListNode ptr2 = intersect;while (ptr1 != ptr2) {ptr1 = ptr1.next;ptr2 = ptr2.next;}return ptr1;}
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

例题

142. 环形链表 II

题目描述

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从0开始)。 如果 pos-1,则在该链表中没有环。

说明:不允许修改给定的链表。

  • 难度:Medium

解题思路

经典的 Floyd 算法的应用场景。

public class Solution {public ListNode detectCycle(ListNode head) {if (head == null || head.next == null) return null;ListNode slow = head;ListNode fast = head;while (true) {if (fast == null || fast.next == null) {return null;}fast = fast.next.next;slow = slow.next;if (fast == slow) break;}fast = head;while (fast != slow) {fast = fast.next;slow = slow.next;}return fast;}
}

287. 寻找重复数 (Medium)

题目描述

给定一个包含 n + 1 个整数的数组 nums,其数字都在 1n 之间( 包括 1n ),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。

  • 示例 1:

输入: [1,3,4,2,2]
输出: 2

  • 示例 2:

输入: [3,1,3,4,2]
输出: 3

  • 难度:Medium

解题思路

正常的思路是通过 HashSet , 或者通过排序以迅速找到重复数。

前者的时间和空间复杂度为 O(N),后者排序解决方案的时间复杂度为 O(NlogN) 空间复杂度为 O(1)

可以取巧的是,这道题因为题目的关系,可以将题目中数组视为 索引对应值 的关系视为一个 链表,因为重复数的关系,它还是一个 循环链表,因此依然可以通过 Floyd 算法解决:

class Solution {public int findDuplicate(int[] nums) {int fast = nums[0];int slow = nums[0];// 找到相遇节点while (true) {slow = nums[slow];fast = nums[nums[fast]];if (slow == fast) {}}// 找到重复数int ptr1 = nums[0];int ptr2 = fast;while (ptr1 != ptr2) {ptr1 = nums[ptr1];ptr2 = nums[ptr2];}return ptr1;}
}

参考&感谢

  • LeetCode-142:环形链表
  • Wiki百科:Floyd判圈算法

关于我

Hello,我是 却把清梅嗅 ,如果您觉得文章对您有价值,欢迎 ❤️,也欢迎关注我的 博客 或者 Github。

如果您觉得文章还差了那么点东西,也请通过关注督促我写出更好的文章——万一哪天我进步了呢?

  • 我的Android学习体系
  • 关于文章纠错
  • 关于知识付费
  • 关于《反思》系列

弗洛伊德的乌龟与兔子相关推荐

  1. 快慢指针(LeetCode寻找重复数),弗洛伊德的乌龟和兔子

    写此篇博客在于总结,记忆之用,欢迎评论补充. 弗洛伊德的乌龟和兔子,即快慢指针. 对于LeetCode287题,寻找重复数,题目如下: 给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 ...

  2. 弗洛伊德的乌龟和兔子(循环检测

    内容参考:https://www.jianshu.com/p/88c7e7a8de61 简短概述一下,就是一个大小为n+1的数组中,每个元素的大小范围为[1,n],该数组有且仅有一个重复数字,找出这个 ...

  3. 「兔了个兔」龟兔赛跑——乌龟和兔子能否相遇?

    前言 森林里一年一度的赛跑大赛又开始了,乌龟和兔子又被分到了一组,这次乌龟还能跑过兔子吗? 比赛规则: 乌龟每次移动1个单元格,兔子每次移动2个单元格. 随机出现一条赛道,赛道可能为直线,也可能为含有 ...

  4. //多线程龟兔赛跑问题,乌龟和兔子比赛跑200米的距离,//名叫兔子的线程每执行一次兔子就向前跑5米,每跑50米休息10毫秒,//名叫乌龟的线程每执行一次乌龟就向前跑1米,乌龟会一直跑不休息/

    //多线程龟兔赛跑问题,乌龟和兔子比赛跑200米的距离, //名叫兔子的线程每执行一次兔子就向前跑5米,每跑50米休息10毫秒, //名叫乌龟的线程每执行一次乌龟就向前跑1米,乌龟会一直跑不休息 // ...

  5. 【龟兔赛跑: 1、总里程100米 2、兔子每秒跑5米,每20米要休息2秒 3、乌龟每秒钟4米,不休息 4、谁先到达终点,比赛结束】

    public class Zuoye2 {public static void main(String[] args) {//兔子的线程Thread rabbit = new Thread() {@O ...

  6. php实现狼和兔子,兔子和狼作文4篇

    导语:下面兔子和狼,各位同学们会想出怎样精彩的故事呢?是小编为大家整理的兔子和狼作文,供大家阅读. 兔子和狼作文[1] 春天来了,暖融融的阳光驱走了寒冷,和煦的春风轻吻着大地.花儿开了,草儿绿了.一只 ...

  7. 【Leetcode】刷题之路2(python)

    哈希映射类题目(简单题小试牛刀啦bhn) 242.有效的字母异位词 349.两个数组的交集 1002.查找常用字符 202.快乐数 383.赎金信 242. 有效的字母异位词 用python的Coun ...

  8. 蓝桥杯训练题目若干(东华20考研挑战题1-50)

    文章目录 前言 1 Huffuman树 2 回文数 3 字母图形 4 大阶乘计算 5 回形取数 6 龟兔赛跑预测 7 Sine之舞 8 矩形面积交 9 矩阵乘法 10 分解质因数 11 字符串对比 1 ...

  9. PTA 基础编程题目集 7-22 龟兔赛跑 C语言

    PTA 基础编程题目集 7-22 龟兔赛跑 C语言 乌龟与兔子进行赛跑,跑场是一个矩型跑道,跑道边可以随地进行休息.乌龟每分钟可以前进3米,兔子每分钟前进9米:兔子嫌乌龟跑得慢,觉得肯定能跑赢乌龟,于 ...

  10. 用C语言解“龟兔赛跑”题

    7-22 龟兔赛跑 乌龟与兔子进行赛跑,跑场是一个矩型跑道,跑道边可以随地进行休息.乌龟每分钟可以前进3米,兔子每分钟前进9米:兔子嫌乌龟跑得慢,觉得肯定能跑赢乌龟,于是,每跑10分钟回头看一下乌龟, ...

最新文章

  1. 基于JSP/SERVLET实现的人脸识别考勤系统
  2. tomcat 和 jdk 版本 对应关系
  3. 【Android Gradle 插件】Android Plugin DSL Reference 离线文档下载 ( GitHub 下载文档 | 查看文档 )
  4. Linux中通过mkdir –p 能够创建多级目录(mkdir -p详解)
  5. 爱了!爱了!Markdown 必备组合神器!
  6. zookeeper集群配置说明以及window单台机器集群搭建
  7. 有关Botton的用法(二)
  8. 2016030206 - mysql常用命令
  9. sparkstreaming 读取mysql_SparkStreaming读取Kafka的两种方式
  10. kafka是如何解决粘包拆包的
  11. bzoj2150,poj1422,poj1548
  12. Linux查看文件第几行到第几行命令
  13. Android中继承的Dialog导致程序崩溃
  14. 如何在屏幕实时显示键盘操作(独家分享)
  15. slot的使用方法详解
  16. MTSP问题遗传算法解决及其代码与案例
  17. 如何用公式编辑器打半中括号?
  18. main java.lang,各位大哥办我看看,Exception in thread main java.lang.Error: 无法解析的编译有关问题...
  19. 【问链-区块链基础知识系列】 第十二课 区块链产业落地现状分析
  20. python 查看处理器架构

热门文章

  1. 财务管理系统-数据库模块
  2. Cesium orientation 和 设置初始角度
  3. 解决仙剑奇侠传“应用程序无法正常启动(0xc000000d)”的问题【转载】
  4. 空间相册怎么移到计算机里,qq空间上传照片_怎样把电脑里存的照片传到qq空间??...
  5. 谷歌AI平均每天发表2篇论文!Jeff Dean执笔年度汇总:16大方向
  6. Android 修改wifi阀值,6种简单方法使WiFi网络提速
  7. 【编程之美】读书笔记:寻找最大的K个数
  8. linux 搭建技术博客,Linux NTP服务器搭建精讲
  9. 亚马逊云服务(AWS)机器学习服务Amazon SageMaker发力中国
  10. 高通Hexagon通用计算DSP介绍