不修改数组找出重复的数字
剑指offer
在一个长度为n+1的数组里面的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为9的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。
方法一
创建一个新n+1的数组data,遍历原来的数组如 2 将2存到 data[2]中, 3存到data[3]中…. 等下下次一遍历到3 发现data[3]=3 说明重复了。
`
public static void main(String[] args) {
int [] array={2,3,5,4,3,2,6,7};
//创建一个新的数组
int [] newArray=new int[array.length];
Map<Integer,Integer> map=new HashMap<>();
for(int i=0;i<newArray.length;i++){
//赋值为-1
newArray[i]=-1;
}
for(int i=0;i<array.length;i++){
if(newArray[array[i]]==array[i]){
map.put(array[i],map.get(array[i])+1);
}else{
newArray[array[i]]=array[i];
map.put(array[i],1);
}
}
map.forEach((key,value)->{
if (value>1){
System.out.println(key +" : "+value);
}
});
`
结果:
分析:
统计个数是我自己加上去的
算法时间复杂度为 O(n),空间复杂度O(n)
方法二
使用二分法。 如:{2,3,5,4,3,2,6,7} 先二分,先统计在1~4里面的数字个数如果大于4则说明1~4里面有重复数字,否则5~7里面有重复数字。重复上面操作。
`
public static void main(String[] args) {
int [] array={2,3,5,4,3,2,6,7};
int length=array.length-1;
int start=1,end=length;
while(start<=end){
//取中
int middle=((end-start)>>1)+start;
int count= getCount(array,start,middle);
if(start==end){
if(count>1){
//输出重复数字
System.out.println(start);
break;
}else {
break;
}
}
if(count>(middle-start+1)){
end=middle;
}else{
start=middle+1;
}
}
}
//查找数组中值位于start和end之间的元素个数
public static int getCount(int[] array,int start,int end){
if (array==null)
return 0;
int count=0;
for(int i=0;i<array.length;i++){
if(array[i]>=start&&array[i]<=end){
count++;
}
}
return count;
}
`
结果:
分析:二分查找logn ,但是getCount每个数组遍历一变 n,时间复杂度为O(nlogn),空间复杂度为O(1)。但是这个方法有个问题不能找出所有的重复元素。
如{2,2,4,4,5,5} 找到的元素会是4。
start=1,end=5,middle=3。 1~3中元素有2个则4~5有重复
start=4,end=5,middle=4。 4有2个元素
start=4,end=4,middle=4, 返回4。