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个整数的数组,包含1~n(包含n),找出重复的数字
附加条件:
不能修改数组,也就是不能用排序
只能用O(1) 的额外空间,也就是不能用hashset之类的保存数字
runtime小于O(n2),也就是不能用brute force搜索
思路:
本质上是链表中存在环,找环入口的问题。
为什么是链表的环?且把数组中每个数字看作数组下标(因为本来应该是1~n的下标,对应1~n的数字)来看。
以前在链表中走一步是把指针右移,即指针++,
这里的走一步是找到nums中对应下标的数字,即nums[pointer],走两步就是nums[nums[pointer]]。
可以看example1: [1,3,4,2,2]
从下标0开始,nums[0] = 1, 然后nums[1] = 3, nums[3] = 2, nums[2] = 4, nums[4] = 2,
看到这里出现了环,nums[3] == nums[4]。
找到环的入口在142. 链表环中见过,
用快慢指针,快指针每次走两步,慢指针每次走一步,它们相遇就是存在环。
然后慢指针回到起点,快慢指针每次都走一步,它们相遇的地方即是环的入口,也就是重复的元素。
public int findDuplicate(int[] nums) {
int n = nums.length;
int slow = 0;
int fast = 0;
while(true) {
slow = nums[slow];
fast = nums[nums[fast]];
if(slow == fast) break;
}
slow = 0;
while(true) {
slow = nums[slow];
fast = nums[fast];
if(slow == fast) break;
}
return slow;
}
参考 Floyd算法