《剑指offer-第二版》-面试题03-数组中重复的数字-02-不修改数组找出重复的数字(Java)


点击查看: 《剑指offer-第2版》 全部面试题 详解目录(Java版)


不修改数组找出重复的数字

题目描述: 在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为8的数组{2 , 3 , 5 , 4 , 3 , 2 , 6 , 7 },那么对应输出的是重复的数字2或3

  • 思路一: 复制数组,按照03_01的方法手动排序。 时间O(n) 空间O(n)
  • 思路二: 使用hashmap 时间O(n) 空间O(n)
  • 思路三: 使用二分查找的思想,因为数字为1-n.
    • 我们把1~n的数字从中间的数字m分为两部分,前面一半为1 ~ m,后面一半为m+1 ~ n。 如果 1 ~ m的数字的数目超过m,那么这一半的区间里一定包含重复数字;否则,另一半 m+1 ~ n 的区间里一定包含重复的数字。我们可以继续把包含重复区间数字的区间一份为二,直到找到一个重复的数字。这个过程和二分查找算法很类似,只是多了一步统计区间里数字的数目。
    • 如果输入长度为n的数组,那么计数函数执行O(logn)次,每次需要O(n)的时间,因此总的时间复杂度为O(nlogn),空间复杂度为O(1)

注意:

  • start end middle 均为数组中最小值,最大值,(max-min)/2+start的值
  • 目的是为了找出,某个重复的数字,故是对数组中最大和最小的数字范围进行二分 而非数组区间
package ch02;

/**
 * @author Ren
 */
public class T03_2_不修改数组找出重复的数字 {

    public static void main(String[] args) {
        int [] arr = {2,5,4,3,2,6,7};
        int ans = getDuplication(arr,arr.length);
        System.out.println(ans);
    }

    static int getDuplication(int [] arr, int length){
        if(arr==null || length <= 0){
            return -1;
        }
        // 找到数组中的最大值和最小值
        int min=length,max = 1;
        for (int i = 0; i < length; i++) {
            min=min>arr[i]?arr[i]:min;
            max=max<arr[i]?arr[i]:max;
        }
        int start = min;
        int end = max;
        while (end >= start){
            int middle = ((end-start)>>1) + start;
            int count = countRange(arr,length,start,middle);
            // 当start==end 则找到重复的目标,返回数据
            if(end==start){
                if(count>1){
                    return start;
                }else{
                    break;
                }
            }
            // 若 count>(middle-start+1) 该区间(数字大小区间 不是数组的区间)内有重复元素 下次循环的end=middle
            if(count>(middle-start+1)){
                end = middle;
            }else{ // 否则 对后半个区间进行统计  start = middle+1
                start = middle + 1;
            }
        }
        return -1;
    }

    /**
     * 统计区间元素在数组中出现的次数的方法
     */
    static int countRange(int [] arr,int length,int start,int end){
        if(arr==null){
            return 0;
        }
        int counts = 0;
        for (int i = 0; i < length; i++) {
            if(arr[i]>=start && arr[i]<=end){
                counts++;
            }
        }
        return counts;
    }
}

上一篇:ftp:linux下利用shell脚本添加虚拟用户并赋予权限


下一篇:9-算法 归并排序