一.题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
二.解题思路
1.常规解法:
for循环遍历
public int minArray(int[] numbers){
/*
* 时间复杂度O(n),空间复杂度O(1)
* */
if(numbers == null || numbers.length == 0){
return -1;
}
for (int i = 0; i < numbers.length - 1; i++) {
if(numbers[i] > numbers[i + 1]){
return numbers[i + 1];
}else{
continue;
}
}
return numbers[0];
}
2.二分法
旋转数组虽然不是完全升序或者降序,但是也可以用二分查找。满足左半边升序,右半边升序的特点,最小元素在两个有序数组的中间。这样的话每次能大概去除一半的元素比较。
为什么不把pre和mid相比来缩短区间?
因为当pre<mid的时候无法确定最小数字是在左区间还是右区间。由于翻转的原因可能在右区间,但是由于左区间的递增特性也可能是在左区间
public int minArray1(int[] numbers){
/*
* 时间复杂度O(logn),特殊情况下如[1,1,1,1,1]退化到n,空间复杂度O(1)
* */
if(numbers == null || numbers.length == 0){
return -1;
}
int pre = 0;
int end = numbers.length - 1;
while(pre <= end){
int mid = (pre + end) / 2;
//mid在右半边升序数组,最小数字一定在pre-mid区间(mid或者mid左边对应的数字)
if(numbers[mid] < numbers[end]){
end = mid;
//mid在左半边升序数组,最小数字一定在mid+1-end区间
}else if(numbers[mid] > numbers[end]){
pre = mid + 1;
//end--不影响最小值结果,因为mid还在
}else{
end--;
}
}
//end可能会错过最小数字,但是pre一定不会
return numbers[pre];
}