Algorithm | Floyd循环检测算法
Algorithm | Floyd循环检测算法
一、背景
Floyd 循环检测算法(又称龟兔赛跑算法),由Robert W.Floyd于1967 年提出,用于检测链表中是否存在环,并找到环的起点。
二、算法设计
2.1问题定义
Floyd循环检测法要解决什么问题?
- 判断一条链路中是否有环?
- 如果有环,那么环的入口是哪里?
2.2算法思路
如果一条链表存在环路,那么一快一慢两个指针一定会相遇,我们假设快指针一次走两步,慢指针一次走一步,最终相遇。那么,相遇时快指针走的距离是慢指针距离的两倍,这其中应该有一些数学关系可以推导,我们来看一下。
2.3算法设计
要实现这个算法,需要分两步走:
- 快慢指针检测环
- 双指针确定“入环点”
步骤一:检测环
(1)初始化快慢指针
- 慢指针slow指向链表头结点
- 快指针fast指向链表头结点
(2)移动指针
- 慢指针每次移动1步,slow = slow.next
- 快指针每次移动2步,fast = fast.next.next
- 重复此过程直到:
- 快指针=null,表示链表没有环路
- 快指针=慢指针,表示链表有环路
步骤二:寻找环的入口
(1)重置指针
- 慢指针slow指向链表头结点
- 快指针在相遇点不变
(2)同步移动
- 慢指针每次移动1步,slow = slow.next
- 快指针每次移动1步,fast = fast.next
- 循环上面的步骤,直到快慢指针再次相遇,相遇点即为“入环点”,理由参考2.2中图示
三、代码实现(Java)
3.1Java实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}
public class CycleDetection {
/**
* 检测链表是否有环并返回环的入口节点
* @param head 链表头节点
* @return 环的入口节点,如果无环返回null
*/
public static ListNode detectCycle(ListNode head) {
if (head == null || head.next == 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) {
// 有环,进入第二阶段:寻找环入口
slow = head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow; // 环的入口节点
}
}
return null; // 无环
}
}
本文由作者按照 CC BY 4.0 进行授权
