剑指 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)
如有错误,欢迎指正!