题目描述:在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。
难度:二星
思路1:时间复杂度O(n)空间复杂度O(1)
如果数组中没有重复的数组,重排后,则下标index和下标所在的数字应该是相等的,即所有num[index]==index;否则,在数组中必然存在一组数x,y,num[x]=x;num[y]=x。
重排时规则:
扫描整个数组,假设num[i]==i,即这个数在它该有的位置,那么扫描下一个;
如果num[i]!=i,我们把num[i]应该在它该有的位置,既num[num[i]]=num[i],
此时如果num[i] == num[num[i]],说明number[i]已经出现过,那就找到了重复的数字;
此时如果num[i] != num[num[i]],那就把num[i]放到number[num[i]]上去,处理num[num[i]],直到重复或者与i相等。
// JAVA
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
if(numbers==null || numbers.length==0) {
duplication[0]=-1;
return false;
}
for(int i=0;i<numbers.length;i++) { //遍历数组
while(numbers[i]!=i) { // 直到找到num[i]=i,才继续比较下一个
if(numbers[i]==numbers[numbers[i]]) { //若存在重复元素
duplication[0]=numbers[i];
return true;
}
swap(numbers,i,numbers[i]);
}
}
return false;
}
private static void swap(int[] num,int i,int j) {
int tmp=num[i];
num[i]=num[j];
num[j]=tmp;
}
}
其他思路:利用Hash或Set集合,时间复杂度O(n), 空间复杂度O(n);
习题拓展:不修改数组找到重复数字,要求空间O(1)
不修改数组找出重复的数字。在一个长度为n+1的数组中的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。在不修改输入数组的情况下找出数组中任意一个重复数字。例如输入长度为8的数组{2, 3, 5, 4, 3, 2, 6, 7},则对应输出的是2或者3。
显然Hash和Set方法是不能用了。
解题思路:二分法;对于一个整数范围i~j,如果在这个范围内的整数的数量超过j-i+1,那么此范围内的整数中必然有整数会出现多次。因此选定范围1~n/2,并记录输入数组中在此范围内的元素的数量,若数量大于n/2,则在此整数范围内必然有整数在数组中出现了多次,否则在范围(n/2)+1~n内必然有整数在数组中出现多次。对确定出现重复整数的范围再进行前面的划分和计数操作,直到找到重复的数字。
偷懒的思路:先排序,再二分查找