剑指 Offer 11. 旋转数组的最小数字

原题链接

分析

有题目可以知道找的是数组中的最小值,最容易想到的方法应该就是直接遍历数组,使用一个变量保存数组中的最小值

class Solution {
    public int minArray(int[] numbers) {
        if(numbers.length == 0) return 0;
        int res = numbers[0];
        for(int i : numbers) res = Math.min(res, i);

        return res;
    }
}

上述的这种做法的时间复杂度是线性,即O(n)的。

优化

由于题目中初始的数组是递增的数组,然后再某一个位置旋转了,使得数组前面的数到后面了

  • 由此我们可以知道我们数组中最后一个值x,会使得最小值左边的数都是大于等于x的,最小值右边的数都是小于等于x的,所以可以利用这个特性。
  • 但是在此处由于再这两个区间中都是存在等于x的数,会使得我们选取的中间点如果和x相等的时候,不知道这个值是在最小值的哪边(二分查找最重要的就是要明白每次我的答案应该在那个区间中),所以开始可以先去重就可以了。
  • 上面的条件达成了就可以利用二分法查找边界了
class Solution {
    public int minArray(int[] numbers) {
        int l = 0, r = numbers.length - 1;
        while(r > 0 && numbers[r] == numbers[l]) r --;
        //下面成立说明了剩下的序列就是有序的(这句话也可以不加,但是后面的基准点就不能选错了)
        if(numbers[l] < numbers[r]) return numbers[0];
        int x = numbers[r];
        while(l < r){
            int mid = l + r >> 1;
            if(numbers[mid] <= x) r = mid;
            else l = mid + 1;
        }

        return numbers[l];
    }
}

上述的算法平均时间复杂度为O(logn),在最坏的情况下为O(n)

如有错误,欢迎指正!
原文地址:https://www.cnblogs.com/Lngstart/p/14717608.html