算法描述
Floyd判圈算法,又称快慢指针算法、龟兔赛跑算法,是一个可以在有限状态机、迭代函数或者链表上判断是否存在环,求出该环的起点与长度的算法。
场景1 判断是否存在环
设想在一个链表上初始位置head位置之前位置有乌龟和兔子,乌龟每次移动一个节点距离,兔子是乌龟的两倍,此时兔子速度是乌龟的2倍,当乌龟进入环后在走完一圈之前或者恰好走完一圈时与兔子相遇。
注意:兔子与乌龟速度必须为 2:1 不然兔子与乌龟在环中可能永远无法相遇或者需要经过很多圈才能相遇,相遇实机不可控。
典型例题:
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
示例 1:
输入: head = [3,2,0,-4], pos = 1
输出: true
解释: 链表中有一个环,其尾部连接到第二个节点。
提示:
链表中节点的数目范围是 [0, 104]
-105 <= Node.val <= 105
pos 为 -1 或者链表中的一个 有效索引 。
进阶:你能用 O(1)(即,常量)内存解决此问题吗?
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode slow = head;
ListNode fast = head.next;
while (slow != fast) {
if (fast == null || fast.next == null) {
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}
}
场景2 求环的起点
如上图所示slow与fast速度比值为 1:2; S点到P点距离为 m, P点按照顺时针到M点距离为 n ,M点顺时针到P点距离为 o, 当slow指针与fast指针首次相遇后,将fast指针停止不动,让slow继续行走并开始从0记录距离,当slow与fast再次相遇时slow走过的长度几位环的长度。
场景3 求环的长度
如上图所示,当fast与slow首次相遇可得
- slow走过距离为 m + n
- fast走过距离为 m + (n+o) * N + n
- 又因 slow速度 * 2 = fast速度 可推导出fast与slow首次相遇时距离关系
- 2 *(slow走过距离)= fast走过距离
=> 2 * (m+n) = m+n + (n+o) * N
=> m = o + (N-1) * (n+o)
由 m = o + (N-1) * (n+o) 结合上图可知,如果在起点S处、相遇点M点分别放置一个指针,让两指针以相同的速度(每次1步)向前走,当两指针相遇时,相遇点即为环入口点