Floyd判圈算法 Floyd Cycle Detection Algorithm
2018-01-13 20:55:56
Floyd判圈算法(Floyd Cycle Detection Algorithm),又称龟兔赛跑算法(Tortoise and Hare Algorithm),是一个可以在有限状态机、迭代函数或者链表上判断是否存在环,求出该环的起点与长度的算法。该算法据高德纳称由美国科学家罗伯特·弗洛伊德发明,但这一算法并没有出现在罗伯特·弗洛伊德公开发表的著作中。
如果有限状态机、迭代函数或者链表上存在环,那么在某个环上以不同速度前进的2个指针必定会在某个时刻相遇。同时显然地,如果从同一个起点(即使这个起点不在某个环上)同时开始以不同速度前进的2个指针最终相遇,那么可以判定存在一个环,且可以求出2者相遇处所在的环的起点与长度。
一、算法描述
如果有限状态机、迭代函数或者链表存在环,那么一定存在一个起点可以到达某个环的某处(这个起点也可以在某个环上)。
初始状态下,假设已知某个起点节点为节点S。现设两个指针t和h,将它们均指向S。
接着,同时让t和h往前推进,但是二者的速度不同:t每前进1步,h前进2步。只要二者都可以前进而且没有相遇,就如此保持二者的推进。
- 当h无法前进,即到达某个没有后继的节点时,就可以确定从S出发不会遇到环。
- 反之当t与h再次相遇时,就可以确定从S出发一定会进入某个环,设其为环C。
如果确定了存在某个环,就可以求此环的起点与长度。
环长度:上述算法刚判断出存在环C时,显然t和h位于同一节点,设其为节点M。显然,仅需令h不动,而t不断推进,最终又会返回节点M,统计这一次t推进的步数,显然这就是环C的长度。
环入口:为了求出环C的起点,只要令h仍均位于节点M,而令t返回起点节点S,此时h与t之间距为环C长度的整数倍。随后,同时让t和h往前推进,且保持二者的速度相同:t每前进1步,h前进1步。持续该过程直至t与h再一次相遇,设此次相遇时位于同一节点P,则节点P即为从节点S出发所到达的环C的第一个节点,即环C的一个起点。
环入口算法的证明:
假设慢指针到相遇点的距离为l,则快指针的路程为2l,环的长度为r。
l = x + y;
2l = l + nr;
==> nr = x + y
==> x = nr - y
那么此时,将相遇点的慢指针调到起始点,快指针行进速度和慢指针保持一致,那么当慢指针走了x的路程时,快指针走了nr - y,正好两者在环的入口处相遇。
二、伪代码描述
1 t := &S
2 h := &S //令指针t和h均指向起点节点S。
3 repeat
4 t := t->next
5 h := h->next
6 if h is not NULL //要注意这一判断一般不能省略
7 h := h->next
8 until t = h or h = NULL
9 if h != NULL //如果存在环的话
10 n := 0
11 repeat //求环的长度
12 t := t->next
13 n := n+1
14 until t = h
15 t := &S //求环的一个起点
16 while t != h
17 t := t->next
18 h := h->next
19 P := *t
三、算法复杂度
时间复杂度:注意到当指针t到达环C的一个起点节点P时(此时指针h显然在环C上),之后指针t最多仅可能走1圈。若设节点S到P距离为m,环C的长度为n,则时间复杂度为O(m+n),是线性时间的算法。
空间复杂度:仅需要创立指针t、指针h,保存环长n、环的一个起点P。空间复杂度为O(1),是常数空间的算法。
四、应用
对于有限状态机与链表,可以判断从某个起点开始是否会返回到访问过运行过程中的某个状态和节点。
对于迭代函数,可以判断其是否存在周期,以及求出其最小正周期。
五、相关算法
虽然Floyd判圈算法已经达到了线性时间复杂度和常数空间复杂度,但是Brent判圈算法将减小时间复杂度的常数系数,平均消耗时间比Floyd判圈算法少36%。
Bruent算法描述:
Bruent算法里有运动的兔子和静止的乌龟。这里的乌龟在兔子行进步数到达step_limit时,会传送到兔子的位置,同时将兔子的行进步数重置为0,同时提高step_limit为原来的两倍。
乌龟和兔子都从名单的顶部开始。兔子每迭代一步。如果是和固定的乌龟一样的位置,那显然是一个循环。如果到达列表的末尾,则没有循环。
为什么要移动乌龟呢?如果一只兔子被卡在一个循环中,没有遇到乌龟,它将永远循环,所以需要将乌龟在一定步数后传送到兔子位置。
为什么每次要花两倍的时间?最终,传送之间的时间长度将比回路的长度更长,因此当兔子完成一圈时,乌龟将在那里等待兔子。
Bruent算法的伪代码描述:
1 turtle = top
2 rabbit = top
3
4 steps_taken = 0
5 step_limit = 2
6
7 forever:
8 if rabbit == end:
9 return 'No Loop Found'
10 rabbit = rabbit.next
11
12 steps_taken += 1
13
14 if rabbit == turtle:
15 return 'Loop found'
16
17 if steps_taken == step_limit:
18 steps_taken = 0
19 step_limit *= 2
20 // teleport the turtle
21 turtle = rabbit
六、相关题目摘录
Linked List Cycle
问题描述:
问题求解:
public boolean hasCycle(ListNode head) {
if (head == null) return false;
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) return true;
}
return false;
}
Linked List Cycle II
问题描述:
问题求解:
public ListNode detectCycle(ListNode head) {
if (head == null) return null;
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) break;
}
if (fast == null || fast.next == null) return null;
slow = head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
Happy Number
问题描述:
问题求解:
迭代会生成环,非happy number的环中显然是不可能存在1的,所以如果落入了环中,那么就不可能算得1,否则两者在运算到1后相等。
public boolean isHappy(int n) {
int slow = n;
int fast = n;
do {
slow = calc(slow);
fast = calc(fast);
fast = calc(fast);
}while(slow != fast);
if(slow != 1) return false;
else return true;
} int calc(int n) {
int sum = 0;
while (n > 0) {
int k = n % 10;
sum += k*k;
n /= 10;
}
return sum;
}
Find the Duplicate Number
问题描述:
问题求解:
经典的判圈算法的题目。
public int findDuplicate(int[] nums) {
int slow = 0;
int fast = 0;
do {
slow = nums[slow];
fast = nums[fast];
fast = nums[fast];
} while (slow != fast);
slow = 0;
do {
slow = nums[slow];
fast = nums[fast];
} while (slow != fast);
return slow;
}
Floyd判圈算法 Floyd Cycle Detection Algorithm的更多相关文章
- Floyd判圈算法
Floyd判圈算法 leetcode 上 编号为202 的happy number 问题,有点意思.happy number 的定义为: A happy number is a number defi ...
- SGU 455 Sequence analysis(Cycle detection,floyd判圈算法)
题目链接:http://acm.sgu.ru/problem.php?contest=0&problem=455 Due to the slow 'mod' and 'div' operati ...
- leetcode202(Floyd判圈算法(龟兔赛跑算法))
Write an algorithm to determine if a number is "happy". 写出一个算法确定一个数是不是快乐数. A happy number ...
- Floyd判圈算法 UVA 11549 - Calculator Conundrum
题意:给定一个数k,每次计算k的平方,然后截取最高的n位,然后不断重复这两个步骤,问这样可以得到的最大的数是多少? Floyd判圈算法:这个算法用在循环问题中,例如这个题目中,在不断重复中,一定有一个 ...
- Floyd 判圈算法
Floyd 判圈算法 摘自维基百科, LeetCode 上 141题 Linked List Cycle 用到这个, 觉得很有意思. 记录一下. 链接: https://zh.wikipedia.or ...
- UVa 11549 计算器谜题(Floyd判圈算法)
https://vjudge.net/problem/UVA-11549 题意: 有一个老式计算器,只能显示n位数字,输入一个整数k,然后反复平方,如果溢出的话,计算器会显示结果的最高n位.如果一直这 ...
- UVA-11549(floyd判圈算法)
题意: 给一个整数k,每次平方后只能显示结果的前n位,问在这个过程中能得到的最大的数是多少; 思路: floyd判圈算法;它的正确性建立在这得到的这些数是有限的,所以一定是一个循环,在这个循环的圈里面 ...
- UVA 11549 CALCULATOR CONUNDRUM(Floyd判圈算法)
CALCULATOR CONUNDRUM Alice got a hold of an old calculator that can display n digits. She was bore ...
- UVA 11549 Calculator Conundrum (Floyd判圈算法)
题意:有个老式计算器,每次只能记住一个数字的前n位.现在输入一个整数k,然后反复平方,一直做下去,能得到的最大数是多少.例如,n=1,k=6,那么一次显示:6,3,9,1... 思路:这个题一定会出现 ...
随机推荐
- C的指针疑惑:C和指针17(经典抽象数据类型)
堆栈这种数据最鲜明的特点是:后进先出. 用动态数组实现堆栈: #include "C17.h" #include <stdio.h> #include <stdl ...
- 在MFC里面实现线程的实例
线程是一种从软件到硬件的技术,主要目的是为了提高运行速度,和多任务. ××××××××××××××××××××××××××××××××××××需要储备的资料(他人的)××××××××××××××××× ...
- python 之操作redis数据库(非关系型数据库,k-v)
数据库: 1. 关系型数据库 表结构 2. 非关系型数据库 nosql (k - v 速度快),常用的时以下三种: memcache 存在内存里 redis 存在内存里 mangodb 数据还是存在磁 ...
- quick cocos2d-x 下载地址
https://github.com/chukong/quick-cocos2d-x/tree/master http://www.cocos2dx.net/post/280 配置说明 http:// ...
- vmware下安装centos7
下载vmware http://down-www.newasp.net/pcdown/big/wm_pro_14_win.rar 下载centos7 https://www.centos.org/do ...
- XDU 1164 男神的树(树+lazy数组)
#include<cstdio> #include<cmath> #include<cstring> #include<vector> #define ...
- 移动端web开发 尽量哪些标签 常用标签及注意事项
H5手机移动端WEB开发资源整合 常用的标签及注意事项: https://blog.csdn.net/u012118993/article/details/56023399 移动前端不得不了解的htm ...
- 130. Surrounded Regions(周围区域问题 广度优先)(代码未完成!!)
Given a 2D board containing 'X' and 'O' (the letter O), capture all regions surrounded by 'X'. A reg ...
- hdu2597 Simpsons’ Hidden Talents
地址:http://acm.hdu.edu.cn/showproblem.php?pid=2594 题目: Simpsons’ Hidden Talents Time Limit: 2000/1000 ...
- Java并发包中线程池的种类和特点介绍
Java并发包提供了包括原子量.并发集合.同步器.可重入锁.线程池等强大工具这里学习一下线程池的种类和特性介绍. 如果每项任务都分配一个线程,当任务特别多的时候,可能会超出系统承载能力.而且线程的创建 ...