题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
思路
法1:借助辅助空间:
HashMapkey:某元素,value:某元素出现的次数
统计出所有元素出现的次数,最后找到一个次数>1的元素赋给duplication[0]
(题目只要任意一个重复元素,统计出所有的显然没有必要,所以需要寻找更好的方法)
法2:不借助辅助空间
数组所有元素范围都是0~n-1,数组长度就是n
所有如果数组中没有重复元素的话,数组排序后数字i肯定出现在下标为i的位置
若存在重复元素,则有些位置可能存在多个元素,有些位置上没有元素
- 从头扫描数组中每个数字
- 把下标为i的元素记为m,比较m和下标i。
若m和下标i相等,就继续向后遍历
若不相等,就把m和下标为m的元素比较
(1)若m和下标为m的元素相等,说明该元素在下标为i和m的位置都出现了,找到了一个重复元素
(2)若m和下标为m的元素不等,就把两元素交换,把m放到它应该待的位置上
实现
法1:借助辅助空间:
import java.util.HashMap;
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
HashMap<Integer,Integer> m=new HashMap<>();
//边遍历数组,边加进去元素:<key:某元素,value:某元素出现的次数>
for(int i=0;i<length;i++){
if(!m.containsKey(numbers[i])){ //m里没有某元素,加进去,次数计为1
m.put(numbers[i],1);
}
else{ //m里有某元素,先获取已有的出现次数,再加上1
int count=m.get(numbers[i]);
m.put(numbers[i],count+1); //key相同时,新value覆盖原value
}
}
//寻找value大于1的某个元素
for(int i=0;i<length;i++){
if(m.get(numbers[i])>1){
duplication[0]=numbers[i];
return true;
}
}
return false;
}
}
法2:
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
/*数组所有元素范围都是0~n-1,数组长度就是n
所有如果数组中没有重复元素的话,数组排序后数字i肯定出现在下标为i的位置
若存在重复元素,则有些位置可能存在多个元素,有些位置上没有元素
*/
//特殊情况
if(numbers==null || length<=0) return false;
//从头扫描数组中每个数字
for(int i=0;i<length;i++){
int m=numbers[i]; //把下标为i的元素记为m
//若m和下标i相等,就继续向后遍历
//若不相等,就把m和下标为m的元素比较
while(m!=i){
//若m和下标为m的元素相等,说明该元素在下标为i和m的位置都出现了,找到了一个重复元素
if(m==numbers[m]){
duplication[0]=m;
return true;
}
//若m和下标为m的元素不等,就把两元素交换,把m放到它应该待的位置上
else{
int temp=m;
m=numbers[temp]; //直接写numbers[m]会超时,需要嵌套计算
numbers[temp]=temp; //直接写numbers[temp]效率高,因为temp在上一步得到了,是一个已知的确定值
}
}
}
return false;
}
}
Notes:交换numbers[i]和numbers[numbers[i]],不能直接写numbers[numbers[i]],因为这需要嵌套计算,会超时
可以用交换第一步的temp来代替numbers[i],因为第一步temp就算出来了,是个确定值,所以下一步再用时就剩一层计算了