03-数组中重复的数字
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},重复的是2或者3,输出任意一个即可。
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;
}
int idx = 0;
int repeatNumber = -1;
while (idx < nums.length) {
int curNumber = nums[idx];
if (curNumber == idx) { //如果当前的值与当前的索引一致,说明这个数字和索引是对应的,即number[x] == x这种情况,那么将idx自增即可,查看下一个数字。
idx++;
}else if (nums[curNumber] != curNumber) { //如果当前值与索引值不相同,并且当前值与nums[当前值]也不同,就进行交换,从而使得nums[当前值] == 当前值,这样就可以将nums[当前值]调整好
nums[idx] = nums[curNumber];
nums[curNumber] = curNumber;
}else {
//如果当前值与索引值不相同,并且当前值与nums[当前值]相同,说明发生了重复
repeatNumber = curNumber;
break;
}
}
return repeatNumber;
}
我们梳理下代码是如何执行的
初始数组是
idx=0,value=2 , 2不等于0,那么把nums[0]与nums[2]交换,这样可以保证nums[2]的索引和值是相等的,交换后数组为
idx=0,value=1,1不等于0,那么把nums[0]与nums[1]交换,交换后数组为
重复比较,这次交换后为
因为前4个的索引和值都对应,那么会一直遍历到nums[4],此时value=2,idx=4,不相等,需要将nums[4]和nums[2]进行交换,但在交换之前我们可以看到nums[2]已经是2了,说明重复了,此时就是查找到了重复的值,返回即可。
四种方法的分析
前三种方法类似,使用了额外的空间。
第四种方法直接操作了原数组,没有使用额外的空间。