题目:数组中重复的数字
描述:在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
来源:力扣(LeetCode)剑指 Offer 03. 数组中重复的数字
举例:
输入: [2, 3, 1, 0, 2, 5, 3] 输出:2 或 3
题解:
【方法一】直接遍历查找数组中的元素
例如:取出原数组中第一位元素,让其后一位与之比较,如果一轮比较结束没有出现重复元素就继续取出数组中的第二位元素,继续让其后面一位与之比较,知道找出重复元素为止。这种方法效率很低,不推荐使用。
public int findRepeatNumber(int[] nums) {
for (int i = 0; i < nums.length; i++) {
int result = nums[i];
for (int j = i; j < nums.length - 1; j++) {
if(result == nums[j+1]){
return nums[j+1];
}
}
}
return -1;
}
【方法二】也是官方题解,使用Set集合将数组中每一位元素添加到集合中,当添加失败时说明元素重复
注意:set集合添加元素时的返回值为boolean
例如:
原数组为:[2,3,1,0,2,5,3]
numSet.add(2) -> true 添加成功
numSet.add(3) -> true 添加成功
numSet.add(1) -> true 添加成功
numSet.add(0) -> true 添加成功
numSet.add(2) -> false 添加失败,该元素重复出现了,直接返回
public int findRepeatNumber2(int[] nums) {
Set<Integer> numsSet = new HashSet<>();
for (int num : nums) {
if(!numsSet.add(num)){
return num;
}
}
return -1;
}
【方法三】先排序再遍历,排序后重复元素相邻
例如:
原数组为:[2,3,1,0,2,5,3]
排序之后:[0,1,2,2,3,3,5]
每次循环比较其后一位元素是否与前一位相等,就可以判断出是否存在相邻元素重复。
public int findRepeatNumber3(int[] nums) {
//排序
Arrays.sort(nums);
for (int i = 0; i < nums.length-1; i++) {
int num = nums[i];
if(nums[i+1] == num){
return nums[i+1];
}
}
return -1;
}
【方法四】使用一个临时数组,记录数组元素出现的索引,遍历原数组,每出现一次元素,就将新数组索引位置的值设置为1,当元素值第二次出现的时候,判断新数组的该索引处的值是否为0,如果不是,那么这就是重复元素。
例如:
原数组为:[2,3,1,0,2,5,3]
临时数组:[0,0,0,0,0,0,0]
第1次循环:[0,0,1,0,0,0,0]
第2次循环:[0,0,1,1,0,0,0]
第3次循环:[0,1,1,1,0,0,0]
第4次循环:[1,1,1,1,0,0,0]
第5次循环:[1,1,1,1,0,0,0],临时数组索引为2的地方值不为0,说明2之前统计过了,2为重复元素
public int findRepeatNumber4(int[] nums) {
int[] tmpArray = new int[nums.length];
for (int i = 0; i < nums.length; i++) {
if(tmpArray[nums[i]] != 0){
return nums[i];
}
tmpArray[nums[i]]++;
}
return -1;
}
【方法五】创建一个map集合,让原数组中的每一位元素作为key,key对应的值为1,每次put值的时候判断该key对应的值是否为null,如果不为null,说明该key已经put过了,那么这个key所代表的数组中的元素就是重复元素。
例如:
原数组为:[2,3,1,0,2,5,3]
第1次循环:{2,1}
第2次循环:{2,1}、{3,1}
第3次循环:{2,1}、{3,1}、{1,1}
第4次循环:{2,1}、{3,1}、{1,1}、{0,1}
第5次循环:map中key=2已经有元素值了,说明2重复了
public int findRepeatNumber5(int[] nums) {
Map<Integer,Integer> map = new HashMap<>(nums.length);
for (int num : nums) {
if(map.get(num) != null){
return num;
}
map.put(num, 1);
}
return -1;
}
【方法六】使用数组元素与索引对应的方式,由于题目给出的条件是:数组中的所有元素都在0~n-1的范围内,说明数组中的元素不可能大于length,所以我们可以采用交换元素的方式,遍历第一个元素,让他与该元素值索引处的元素进行互换,互换元素后需要重新判断当前位置的元素值是否与当前索引相同,如果相同就跳过,如果不相同就继续交换元素,其次,交换元素之前需要判断如果当前索引对应的值与数组中该值作为索引处的值相等,那么证明这个元素就是重复出现的元素。
例如:
原数组为:[2,3,1,0,2,5,3]
遍历2:[1,3,2,0,2,5,3]
遍历1:[3,1,2,0,2,5,3]
遍历3:[0,1,2,3,2,5,3]
遍历0:值与索引一致,不交换
遍历1:值与索引一致,不交换
遍历2:值与索引一致,不交换
遍历3:值与索引一致,不交换
遍历2:索引为2处的值与当前值一致,说明该值重复,无需交换,直接返回。
public int findRepeatNumber6(int[] nums) {
//[2, 3, 1, 0, 6, 5, 6]
//[1, 3, 2, 0, 6, 5, 6]
//[3, 1, 2, 0, 6, 5, 6]
//[0, 1, 2, 3, 6, 5, 6]
for (int i = 0; i < nums.length; i++) {
//判断当前的值是否与当前索引相等,如果相等就跳过,继续比较下一个位置
if(nums[i] == i){
continue;
}
//如果数组中当前索引对应的值正好与数组中该值的位置的值相等,那就说明这个元素是重复的
if(nums[nums[i]] == nums[i]){
return nums[i];
}
//交换元素
int temp = nums[nums[i]];
nums[nums[i]] = nums[i];
nums[i] = temp;
//每次交换元素过后都继续判断当前索引的值,此处使用i--来抵消每一次循环的i++
i--;
System.out.println(Arrays.toString(nums));
}
return -1;
}