目录
一、一定会相遇的证明
1、如果链表没有环,那么快指针比慢指针先到达尾部(null)。
2、如果链表有环的话,因为快指针走的比慢指针快,所以在环中相遇的过程可以看作是快指针从环后边追赶慢指针的过程。
用递归法证明,快慢指针一定会相遇:
(1)快指针与慢指针之间差一步。此时继续往后走,慢指针前进一步,快指针前进两步,两者相遇。
(2)快指针与慢指针之间差两步。此时继续往后走,慢指针前进一步,快指针前进两步,两者之间相差一步,转化为第一种情况。
(3)快指针与慢指针之间差N步。此时继续往后走,慢指针前进一步,快指针前进两步,两者之间相差(N+1-2)即N-1步。重复这个过程,直到快指针和慢指针相遇。
因此,此题得证。所以快指针必然与慢指针相遇。
推导:慢指针进入环后,快指针最多多绕一个圈。
快指针F先进环,慢指针S后进。
假设慢指针进环那一刻快指针差m步能追上(0<= m < Length环),根据上边结论,两个指针走m次就会相遇了。
因为m < Length环,所以快指针在慢指针进环那一刻最多比慢指针多绕一个圈。
二、环长度
快指针和慢指针第一次相遇时的节点pq(碰撞点),快指针和慢指针从该点开始继续往前走,再次碰撞时所用的操作数就是环的长度Length环。
证明:由上边的推导可得,这里的m为Lengh环。
三、连接点
假设慢指针进入环中时,即连接点p,快指针(q)需要m步才能追上慢指针。
p和q第一次相遇时,碰撞点在pq处。此时,p走到pq时用了m步。
假设head到p的距离为a,环长度为Length环,慢指针走了s步,则快指针走了2s步。
从上图可知:
s = a + m
2s = a + m + n * Length环(n为快指针绕环的圈数)
可得
a = n * Length环 - m
也就是:若在头结点和相遇结点分别设一指针,同步(单步)前进,则最后一定相遇在环入口结点p。
可根据这个结论来找到入口节点。
四、带环链表总长度
找到连接点p后,求head到p的长度,再加上环的长度,即为链表的总长。
五、例题
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2]
输出: 2
示例 2:
输入: [3,1,3,4,2]
输出: 3
说明:
- 不能更改原数组(假设数组是只读的)。
- 只能使用额外的 O(1) 的空间。
- 时间复杂度小于 O(n2) 。
- 数组中只有一个重复的数字,但它可能不止重复出现一次
【代码】
class Solution {
public:
int findDuplicate(vector<int> &nums) {
int s = nums[0];
int f = nums[nums[0]];
while(s != f){
s = nums[s];
f = nums[nums[f]];
}
f = 0;
while (f != s)
{
f = nums[f];
s = nums[s];
}
return f;
}
};