leetcode习题287 Find the Duplicate Number 在答案中看到了floyd’s tortoise and hare 算法,知道了如果有限状态机、迭代函数或者链表存在环,那么是需要算法检测环是否存在。检测算法有三种:Floyd龟兔算法、Brent算法、Gosper算法。
Floyd龟兔算法
算法描述
Floyd龟兔算法是一种指针算法。该算法仅使用移动速度不同的两个指针就能检测出是否有环。Floyd龟兔算法解决以下问题:1检测是否有环。2环的起点节点。3环的长度。
1 检测是否有环。
想象在一个环形跑道上跑步,两个人同时出发,出发以后速度快的人终究会在某一点和速度慢的人相遇。一般这个时候相遇,速度快的人比速度慢的人至少多跑一圈。
我们假设列表的开始节点是S,环上的起始节点是P,第一次相遇的节点是M。S和P的距离是p,从P和M的距离是m,从M到p的距离是n。指针t和h初始状态下都指向S。接着t每次只有1步,h每次走2步。只要二者没有相遇,就一直按着这个速度走下去。当h无法前进(到达队列末尾)的时候,可以判断没有环。如果t和h在某点再次相遇,则确定有环。
举一个迭代函数的例子。有函数f定义域和值域都是 S = {0,1,2,3,4,5,6,7,8},从
x
0
=
2
x_0=2
x0=2开始,不断重复调用f,能够产生一个序列:2,0,6,3,1,6,3,1,6,3,1…产生了一个环:6,3,1。
定义:S是一个有限集合,f是一个函数,从S到S的一个映射,
x
0
x_0
x0可以是S中的任意一个元素。对于任意的
i
>
0
i>0
i>0,
x
i
=
f
(
x
i
−
1
)
x_i=f(x_{i-1})
xi=f(xi−1)。
μ
\mu
μ是换上的起点节点的最小下标,
λ
\lambda
λ是环的长度。一定有
x
μ
=
x
λ
+
μ
x_\mu=x_{\lambda+\mu}
xμ=xλ+μ
证明:如果有环存在,则对于任意的整数
i
>
=
μ
i>=\mu
i>=μ并且
k
>
0
k>0
k>0,都有
x
i
=
x
i
+
k
λ
x_i=x_{i+k\lambda}
xi=xi+kλ。对于特定的k来讲,一定存在使得
i
=
k
λ
i=k\lambda
i=kλ,那这时候
x
i
=
x
2
i
x_i=x_{2i}
xi=x2i。至此,说明了速度不同的两个指针可以在某点相遇。
2 计算环长度
当t和h相遇在M点。因为相遇的点一定在环上。这时候保存h不动,t按之前的速度继续前进,直到和h再次相遇,这个过程中移动的步数就是环的长度。
3 环的起始节点确定
在确定是否有环的过程中,h走的距离是t走的距离的2倍。c为环长。t走的距离是
s
1
=
p
+
m
+
a
∗
c
s1=p+m+a*c
s1=p+m+a∗c,h走的距离是
2
∗
s
1
=
p
+
m
+
b
∗
c
2*s1=p+m+b*c
2∗s1=p+m+b∗c,两式子相减得到:
s
1
=
(
b
−
a
)
∗
c
=
p
+
m
+
a
∗
c
s1=(b-a)*c=p+m+a*c
s1=(b−a)∗c=p+m+a∗c,得到p+m=环的整数倍。
为了找到环的起点,t回到起点,h在当前位置。同时向前,他们再次相遇一定在P点。为什么呢?因为从S到P的距离是p,从P到M的距离是m,因为m+p是环长的整数倍,所以当h走过距离p的时候也一定达到了P点。
算法时间复杂度:令S到P的距离为m,环的长度为n,时间复杂度
O
(
m
+
n
)
O(m+n)
O(m+n)。
空间复杂度:O(1)。