剑指offer

02_不修改数组找出重复数字

2020-05-17  本文已影响0人  是新来的啊强呀

要求:

在一个长度为n+1的数组里的所有数字都在1到n的范围内。 数组中至少有一个数字是重复的,请找出数组中任意一个重复的数字,但不能修改输入的数组;

思路:

数组长度为n+1,而数字只从1到n,说明必定有重复数字。可以由二分查找法拓展:把1--n的数字从中间数字m分成两部分,若前一半1--m的数字数目超过m个,说明重复数字在前一半区间,否则,在后半区间m+1~n。每次在区间中都一分为二,知道找到重复数字。

public int getDuplication(final int[] numbers, int length){
        if(numbers == null||length<=0){
            return -1;
        }
        int start = 1;
        int end = length-1;
        while(end>=start){
//            int middle = ((end+start)/2);    // 相当于下式
            int middle = ((end-start)>>1)+start;  // 右移1位相当于处于2
            System.out.println("+++"+middle);   // 输出中间索引值
            int count = countRange(numbers,length,start,middle);  // 计算前半段的数目个数
            if(end == start){
                if(count>1){
                    return start;
                }else{
                    break;
                }
            }
            if(count>=(middle-start+1)){  // 若前一半1--m的数字数目超过m个,说明重复数字在前一半区间
                end = middle;
            }else{  // 否则在后半区
                start = middle+1;
            }

        }
        return -1;
    }
    // 计算numbers[]中start到end的数字的个数,例如[1,2,3,4,3]中属于1到4的数目为5个。
    public int countRange(int[] numbers,int length,int start,int end){
        if(numbers == null){
            return 0;
        }
        int count = 0;
        for(int i=0;i<length;i++){
            if(numbers[i]>= start && numbers[i]<=end){
                count++;
            }
        }
        return count;
    }
// 测试
public static void main(String[] args) {
        int[] arr= {2,3,5,4,3,2,6,7};
        L02_getDuplication gd = new L02_getDuplication();
        int d = gd.getDuplication(arr, arr.length);
        System.out.println(d);
    }

时间复杂度O(nlogn),其中(while循环为O(logn),coutRange()函数为O(n))

上一篇下一篇

猜你喜欢

热点阅读