在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
hashmap方法
我们可以用一个hashmap将数组的值和出现的次数保存起来,当遇到值出现的次数大于1时就是重复了,这种方法很简单。
Java的Set方法
我们可以使用集合set的本身能力进行判重处理。如果无法向set中添加元素,说明重复了。
public int findRepeatNumberV2(int[] nums) {
if (nums == null || nums.length == 0) {
return -1;
}
Set<Integer> set = new HashSet<>();
for (int num : nums) {
if (!set.add(num)) {
return num;
}
}
return -1;
}
新建数组方法
我们仔细看下题目会注意到所有数字都在0到n-1的范围内这个条件,一般出现这种条件,我们考虑使用数组解决,因为这个条件限定了数字的范围,可以将数组的索引与数字对应起来。
我们可以初始化一个数组data[n],然后扫描输入的数字,判断data[number]是否是0,假如不是0,说明发生了重复;假如是0,则将该data[number]自增1,表示已经出现该number了。
public boolean duplicate(int numbers[],int length,int [] duplication) {
if(numbers == null || numbers.length <= 1){
return false;
}
int[] value = new int[length];
for (int i = 0; i < length; i++) {
int number = numbers[i];
if (value[number] == 1) {
duplication[0] = number;
return true;
}else{
value[number]++;
}
}
return false;
}
数组原地交换比较方法
和 上面的 新建数组方法的考虑类似,所有数字都在0和n-1的范围内,这个范围和数组的索引范围是一致的,如果没有重复的数字,那么这两个范围应该是一一对应的,但因为有了重复数字,那么就会有多对一的关系。
public int findRepeatNumber(int[] nums) {
if (nums == null || nums.length == 0) {
return -1;
}
for (int i = 0; i < nums.length;) {
int value = nums[i];
if(value != i && nums[value] == value){ //如果值不等于索引i,并且nums[value]的位置正好也是该value,说明重复了。
return value;
} else if (value != i) { //否则 就将nums[value]和nums[i]进行交换
Utils.swap(nums,value,i);
}else{
i++;
}
}
return -1;
}
我们梳理下代码是如何执行的
初始数组是{2,3,1,0,2,5,3}
-
i=0,value=2 , 2不等于0,那么把nums[0]与nums[2]交换,这样可以保证nums[2]的索引和值是相等的,交换后数组为 {1,3,2,0,2,5,3}
-
i=0,value=1,1不等于0,那么把nums[0]与nums[1]交换,交换后数组为{3,1,2,0,2,5,3}
-
重复比较,这次交换后为{0,1,2,3,2,5,3}
-
因为前4个的索引和值都对应,那么会一直遍历到nums[4],此时value=2,索引=4,不相等,需要将nums[4]和nums[2]进行交换,但在交换之前我们可以看到nums[2]已经是2了,说明重复了,此时就是查找到了重复的值,返回即可。
四种方法的分析
前三种方法类似,使用了额外的空间。
第四种方法直接操作了原数组,没有使用额外的空间。