分析:题目一与题目二的区别:题目一没有规定不修改数组,题目一数组中值得范围在0-n-1;考虑题目一中的三种方法,只有方法二没有改变原有数组,所以方法二放到题目二同样的适用;但是我们可以把HashMap替换成数组实现。可以想想为什么题目一不适合使用题目中的方法一,题目二中的方法一与题目一中的方法二思路是一样的。
以下为题目二的解法:
方法一:遍历数组,我们将数组(oldArray)中的值放入另外一个下标等于这个值的数组(tempArray)中,放入的前提是这个数组(tempArray)的此位置的值不等于即将放人的值,如果等于就说明我们找到数组中任意一个重复的数字;
题目一为啥不能使用数组呢?定义一个数组时,如果你不给数组进行赋值,那么数组中的值默认为0;
方法二:如果数组满足题目要求,那么就一定会存在重复的数字。采用二分查找的思想,将数组对半,如果前面一半的个数多于他应该含有数字个数,那么重复的数字肯定在这一部分。否则在另外一部分。如此反复下去就能找到重复的那个数字。
二分查找思想分析图(对照代码):
代码实现如下:
package com.example.offer;
/**
* 在一个长度为n+1的数组里的所有数字都在1-n的范围内,所以数组中至少有一个数字是重复的
* 请找出数组中任意一个重复的数字,但是不能改变输入的数组。
*/
public class DuplicateTest02 {
public static void main(String[] args) {
int[] array=new int[]{2,3,5,4,3,2,6,7};
//int[] array=new int[]{0,3,5,4,3,2,6,7};
//int[] array=null;
//int[] array=new int[]{};
int result=duplicateNum02(array);
if(result!=-1){
System.out.println("查找到的任意重复的数为"+result);
}
}
/**
* 第一种方法:借助另外一个相同大小的数组进行处理
* @param array
* @return
*/
public static int duplicateNum(int[] array){
//避免空指针
if(array==null || array.length<=0){
throw new NullPointerException("您输入的数组为空");
}
//避免输入的数组元素不符合要求
for (int i = 0; i < array.length; i++) {
if(array[i]<=0 || array[i]>array.length-1){
throw new IllegalArgumentException("您输入的数组不符合要求");
}
}
int[] tempArr=new int[array.length];
//循环遍历数组
for (int i = 0; i < array.length; i++) {
if(array[i]==tempArr[array[i]]){
return array[i];
}else{
tempArr[array[i]]=array[i];
}
}
return -1;
}
/**
* 第二种方法:如果数组符合题目要求
* 那么数组中必定会存在重复的数,使用二分查找的思想
* @param array
* @return
*/
public static int duplicateNum02(int[] array){
//避免空指针
if(array==null || array.length<=0){
throw new NullPointerException("您输入的数组为空");
}
//避免输入的数组元素不符合要求
for (int i = 0; i < array.length; i++) {
if(array[i]<=0 || array[i]>array.length-1){
throw new IllegalArgumentException("您输入的数组不符合要求");
}
}
int start=1;
int end=array.length-1;
while(start<=end){
int middle=((end-start)>>1)+start;
int count=countRange(array,start,middle);
if(end==start){
if(count>1){
return start;
}else{
break;
}
}
if(count>(middle-start+1)){
end=middle;
}else{
start=middle+1;
}
}
return -1;
}
/**
* 判断数组中的数值在指定区间的个数
* @param array
* @param start
* @param end
* @return
*/
private static int countRange(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;
}
}
代码反思:第一种:时间复杂度为O(n),空间复杂度为O(n)。第二种:数组长度为n,调用countRange() O(logn)次,每次需要O)(n)时间,所以时间复杂度为O(nlogn),空间复杂度为O(10)。