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.
加入忽略题目对时间复杂度(less than O(N^2))和空间复杂度(O(1))的要求,可以想到有以下几种方法:
(1)直接搜索法
时间复杂度O(N^2),空间复杂度O(1).针对每个元素,向后寻找是否存在其重复元素。
(2)哈希表法
时间复杂度O(N),空间复杂度O(N).
用一个集合/map记录已经遍历过的数,如果已经有了说明是重复数字。(集合中元素不可重复,map中key不可重复)
public int findDuplicate(int[] nums){
Map<Integer,Integer> myMap = new HashMap<Integer,Integer>();
//Set<Integer> mySet = new HashSet<Integer>();
for(int num:nums){
if(myMap.containsKey(num)) return num;
myMap.put(num, 1);
/*
* if(mySet.contains(num)) return num;
* mySet.add(num);
*/
}
return 0;
}
(3)排序法
时间复杂度O(N*logN),空间复杂度O(1)/O(N). (如果不复制数组,则空间复杂度为O(1),但是排序时会改变原数组;复制数组则空间复杂度为O(N)).
public int findDuplicateBySort(int[] nums){
Arrays.sort(nums);
for(int i=1;i<nums.length;i++){
if(nums[i] == nums[i-1]) return nums[i];
}
return 0;
}
public int findDuplicateBySortAndCopy(int[] nums){
int[] newArr = Arrays.copyOf(nums, nums.length);
Arrays.sort(newArr);
for(int i=1;i<nums.length;i++){
if(newArr[i] == newArr[i-1]) return newArr[i];
}
return 0;
}
然后看了网上的一些参考答案,寻找到一些符合题目要求的算法:
(1)二分法
由于这n+1个数字介于1~n之间,首先用二分法选择n/2作为比较对象,当整个数组中小于等于n/2的数字超过n/2,说明1~n/2之间肯定有重复数字,否则n/2~n之间肯定有重复数字,下次在有重复数字的区间内再次使用二分法。
public class Solution {
public int findDuplicate(int[] nums) {
int min = 0, max = nums.length-1;
while(min<=max){
int mid = (max-min)/2+min;
int count = 0;
for(int i=0;i<nums.length;i++)
if(nums[i]<=mid) count++;
if(count<=mid)
min = mid+1;//后半部分有重复
else max = mid-1; //前半部分有重复
}
return min;
}
}
(2)寻找环路法
元素index和元素本身之间存在一个映射关系,从下标0开始,可以产生一个类似链表的序列,如果存在重复元素,则会出现环路,我们就是要寻找环路的起点。
https://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare
有时间多看看==