题目2-不修改数组找出重复的数字
描述:
在一个长度为n+1的数组里的所有数字都在1到n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为8的数组{2, 3, 5, 4, 3, 2, 6, 7},那么对应的输出是重复的数字2或者3。
思路:
1、使用辅助数组实现上面的功能,但是需要额外的空间O(n),数组下标与数字对应。
2、避免使用O(n)的辅助空间。利用类似二分 查找法将数组分为前后两块,利用长度为8的数组{2,3,5,4,3,2,6,7}为例,长度为8的数字都在1~7的范围内,中间数组4把1~7分成左右两端,左边1~4,右边5~7,然后统计左右两边各自出现的次数,若任一边出现次数大于4则认为有重复。在分别统计各自出现的重复次数。
代码实现:
package swordToOffer;
/*在一个长度为n+1的数组里的所有数字都在1到n的范围内,所以数组中至少有一个数字是重复的。
* 请找出数组中任意一个重复的数字,但不能修改输入的数组。
* 例如,如果输入长度为8的数组{2, 3, 5, 4, 3, 2, 6, 7},那么对应的输出是重复的数字2或者3。
*/
public class Num2_DuplicateArray {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arr1 = {2, 3, 5, 4, 3, 2, 6, 7};
int[] arr2 = null;
int[] arr3 = {2, 3, 5, 4, 8, 1, 6, 7};
int[] arr4 = {2, 3, 5, 4, 3, 8, 6, 7};
int result = getDuplicate(arr1);
System.out.println("重复 "+result);
result = getDuplicate(arr2);
System.out.println("重复 "+result);
result = getDuplicate(arr3);
System.out.println("重复 "+result);
result = getDuplicate(arr4);
System.out.println("重复 "+result);
}
public static int getDuplicate(int[] arr) {
if(arr==null||arr.length<=0) {
System.out.println("数组为空 ");
return -1;
}
for(int a:arr) {
if(a<1||a>arr.length-1) {
System.out.println("数组超过规定范围! ");
return -1;
}
}
int start = 1;
int len = arr.length;
int end = len-1;
while(end>=start) {
int mid = ((end-start)>>>1)+start; //使用无符号右移实现除2的效果
int count = countRange(arr,len,start,mid);
if(end == start) {
if(count>1)
return start;
else
break; //出现重复
}
if(count>(mid-start+1)) //左右出现的次数
end = mid;
else
start = mid+1;
}
return -1;
}
private static int countRange(int[] arr,int len,int left,int right){
if(arr==null) return 0;
int count = 0;
for(int i =0;i<len;i++) {
if(arr[i]>=left&&arr[i]<=right) {
++count;
}
}
return count;
}
}
输出结果:
重复 3
数组为空
重复 -1
数组超过规定范围!
重复 -1
数组超过规定范围!
重复 -1
算法分析:
时间复杂度:O(nlogn) 因为函数中使用while(时间复杂度O(logn)),countRange() 调用O(logn)次,每次O(n)时间
空间复杂度:O(1)