Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.
Note:
- You must not modify the array (assume the array is read only).
- You must use only constant, O(1) extra space.
- Your runtime complexity should be less than
O(n2)
. - There is only one duplicate number in the array, but it could be repeated more than once.
看了解析,感觉自己果然还是太年轻。Folyd's Tortoise and Hare在判断一个链表中是否存在环时尤为有效,此算法分为两步,第一步,判断链表中是否存在环,如果不存在,就不用进行第二步,第二步,找到环的入口。
定义两个指针,一个hare,一个tortoise,前者一次走两步,后者一次走一步,这一块一慢,如果链表中存在环,那么二者肯定会相遇,否则的话就说明链表中无环。这便是第一步的过程。
设链表起点为A,环入口为B,环的程度为circle,二者相遇是在点C,点C肯定在环中,这点是肯定的。
那么当tortoise达到点B时,tortoise走过的路程为AB,hare走过的路程为2AB,环中hare的位置D距B(沿着链表的方向)h,则h=AB%circle,当二者相遇时,tortoise又做过了x的距离,那么2x+h-x=circle(因为二者是在同一个环内),x=circle - h,表明tortoise还未走完环的一周就遇到了hare,此时hare距B的距离为h。
接下来执行第二步,将tortoise放回A,将hare的速度减小一倍,当tortoise走完AB时,hare走过的距离也是AB,hare相对于起始位置向前移动了h(h=AB%circle),此时hare刚好位于B,和tortoise相遇。所以这一点就是环的入口。
对于这道题,将索引和索引对应的值看成一个由单链表组成的有多个入口的图的边,那么怎么确定这个图中有没有环呢,如果没有的话就不能用上面的解法了,假设这个图中没有环那么结点个数为n+1,应该有n条边,但是数组中的边的个数为n + 1,说明这个图中肯定存在环。
class Solution {
public:
int findDuplicate(vector<int>& nums)
{
int tortoise = nums[0];
int hare = nums[0];
do{
tortoise = nums[tortoise];
hare = nums[nums[hare]];
}while(tortoise != hare);
int p1 = nums[0];
int p2 = tortoise;
while(p1 != p2)
{
p1 = nums[p1];
p2 = nums[p2];
}
return p1;
}
};
时间复杂度O(n),空间复杂度O(1)。