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... 思路:这个题一定会出现 ...
随机推荐
- 009-Shell 函数
一.函数定义 linux shell 可以用户定义函数,然后在shell脚本中可以随便调用. shell中函数的定义格式如下: [ function ] funname [()] { action; ...
- 两台Linux系统之间传输文件
用CRT分别连上两台需要传输文件的linux系统服务器,并检查防火墙是否关闭. 查看防火墙状态: /etc/init.d/iptables status 若防火墙启用,暂时关闭防火墙: /etc/in ...
- B-、B+、B*树
B-树:多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键字范围的子结点:所有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中: B+树:在B-树基础上,为叶子结点增加链表指针, ...
- 栈的最大值问题 max问题 min问题 队列的max问题
常数时间求栈的最大值 问题描述: 一个栈stack,具有push和pop操作,其时间复杂度皆为O(1). 设计算法max操作,求栈中的最大值,该操作的时间复杂度也要求为O(1). 可以修改栈的存储 ...
- xe7开发的安卓程序,体积宏大--112M!
原因没找到,但似乎可以这样解决: 解决过程:因为代码很少,所以我重新建立一个空白程序,把代码复制过去,一字不差.重新编译, 关键的时刻到了:不要连上真机,在编译完成时,系统提示是否要启动android ...
- SQL Server排名函数与排名开窗函数
什么是排名函数?说实话我也不甚清楚,我知道 order by 是排序用的,那么什么又是排名函数呢? 接下来看几个示例就明白了. 首先建立一个表,随便插入一些数据. ROW_NUMBER 函数:直接排序 ...
- iis 反向代理 组件 Application Request Route
安装后要重启服务器. 不然 IIS 不会生效.
- @Transactional(rollbackFor=Exception.class)的使用
转载: java阿里巴巴规范提示:方法[edit]需要在Transactional注解指定rollbackFor或者在方法中显示的rollback. 先来看看异常的分类 error是一定会回滚的 这里 ...
- JQuery如何实现双击事件时不触发单击事件,解决鼠标单双击冲突问题
在jQuery的事件绑定中,如果元素同时绑定了单击事件(click)和双击事件(dblclick),那么执行单击事件(click)时,不会触发双击事件(dblclick), 执行双击事件(dblcli ...
- C# DateTime 获取时间方法,网上收集
DateTime dt = DateTime.Now; //当前时间 DateTime startWeek = dt.AddDays( - Convert.ToInt32(dt.DayOfWeek.T ...