(java)剑锋Offer(找出数组中任意一个重复的数字不改变原有数组)题目二

分析:题目一与题目二的区别:题目一没有规定不修改数组,题目一数组中值得范围在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)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值