题目:不修改数组找出数组中重复的数字
在一个长度为n的数组里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道哪个数字重复了几次。请找出数组中任意一个重复的数字,但是不能修改输入的数组。例如,如果输入长度为7的数组{2,3,1,9,2,5,3},那么对应的输出是重复的数字2或者3
解决方法
方法一
题目要求不能修改数组,所以我们可以构建一个辅助数组,在方法开始,将输入的数组整个赋值给辅助数组即可,然后再进行辅助数组的修改
但是,Java中传给方法的数组其实是输入数组对象引用的一个拷贝,所以直接在方法中对传入数组进行操作对传入的数据是没有一点问题的
虽然在Java方法中本来就不会对传入数据进行修改,但万一面试官规定要在主方法中进行操作呢,所以我们有了方法二
方法二
假设没有重复数字,那么从 1~n 范围内只有 n 个数字,由于数组中超过了 n 个数字,所以一定会有重复数字
我们可以把 1~n 个数字从中间的数字 m 分成两部分,前面一半为 1~m ,后面一半为 m+1~n。如果前面一半的数字数目超过 m,那么前一半的数字区间里一定包含重复数字,否则,就是后一半的数字区间里包含重复数字。我们继续锁定的区间一分为二,继续判断,直到找到一个数字的数量大于1,就是答案
方法实现
package Test;
public class Question3 {
public static void main(String[] args) {
int[] arr = new int[] {0,1,2,3,6,5,6,6,8,4};
int ret = solution1(arr);
System.out.println(ret);
}
// 方法二:局限性较大
// 不能修改数组中的值,求解
// 将数字进行二分查找
public static int solution1(int[] arr) {
int left = 1;
int right = arr.length-1;
while(left <= right) {
int mid = (left+right)/2;
System.out.println("left="+left+", right="+right+", mid="+mid);
if(right == left) {
if(getNumber(arr, left, right) > 1) {
return left;
}
break;
}
if(getNumber(arr, left, mid) > (mid-left+1)) {
right = mid;
} else {
left = mid+1;
}
}
return -1;
}
// 返回数组arr中 大于等于start 小于等于end 的元素的个数,左闭右开
public static int getNumber(int[] arr, int start, int end) {
int count = 0;
for(int i:arr) {
if(i>=start && i<=end) {
count++;
}
}
return count;
}
}
方法二缺陷
方法二不能保证找出重复的数字,例如
int[] arr = new int[] {2,3,5,4,3,2,6,7};
不能找出这组数据中的重复数字 2,因为在 1~2 数字区间内确实是存在两个数字的。所以,我们用该算法不能确定是每个数字各出现一次还是某个数字出翔两次