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.
Example 1:
Input: [1,3,4,2,2]
Output: 2
Example 2:
Input: [3,1,3,4,2]
Output: 3
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.
思路
包含有n+1个不大于n的正整数的数组一定存在重复元素,本题要求从这样的数组中找出重复的那个元素。题目很简单,有很多方法可做,但是难在要求空间复杂度O(1),时间复杂度小于O(n2),并且源数组是只读的,不允许任何修改操作。这样就限制了诸如Hash表,排序等的解法。最优解法是采用弗洛伊德循环寻找算法,原理如下:
- 需要明确一点:n+1个不大于n的正整数组成的数组可看做一个链表:
- A[0]=1 >>>> A[1]=3 >>>> A[3]=2 >>>> A[2]=4 >>>> A[4]=2
i | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
A[i] | 1 | 3 | 4 | 2 | 2 |
如果这样的数组中出现重复元素,那么这个链表就会出现一个环
- A[0]=1 >>>> A[1]=3 >>>> A[3]=2 >>>> A[2]=4 >>>> A[4]=2
- 弗洛伊德循环寻找算法就是查找这个环入口的算法,过程为:
(1)算法采用了一个慢指针和一个快指针,慢指针每次移动1步,快指针每次移动2步,初始两个指针都指向0,由于路径中存在环,两个指针最终肯定会汇合。第一次汇合时,我们将快指针重置为0,并将移动步数从2修改为1,等到两个指针再次汇合时,指向的元素即为环入口。
(2)原理推导:如下图,P段表示链表中的非环部分,C表示慢指针与快指针第一次汇合时在环中重合的部分,L表是整个环的长度,X表示L-C部分;
(3)则当快指针与慢指针第一次汇合时,慢指针走过的路径长度为P+C,快指针走过的路径长度为L+C+nL(n为整数,由于快指针速度快,因此快指针一定是比慢指针多跑了n圈);然后由于快指针的速度是慢指针速度的2倍,而他们经过的时间是相同的,因此路程有如下关系:
P + C + n L = 2 (