题目
有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。
数据范围:1≤n≤10000,数组中任意元素的值: 0≤val≤10000。
要求:空间复杂度:O(1),时间复杂度:O(logn)。
示例1
输入:[3,4,5,1,2]
返回值:1
示例2
输入:[3,100,200,3]
返回值:3
思路:二分法
因为要求时间复杂度:O(logn),所以考虑二分法。
旋转数组将原本有序的数组分成了两部分有序的数组,因为在原始有序数组中,最小的元素一定是在首位,旋转后无序的点就是最小的数字。可以将旋转前的前半段命名为A,旋转后的前半段命名为B,旋转数组即将AB变成了BA。因为A部分和B部分都是各自有序的,所以可以用分治来解决,每次比较中间值和右界值,逐步缩小范围,确认目标值(最小元素)所在的区间。
具体做法:
- 例1:旋转前:[3,4,5,6,7,8,9];旋转后:[5,6,7,8,9,3,4]
- 例2:旋转前:[3,4,5,6,7,8,9];旋转后:[9,3,4,5,6,7,8]
- 例3:旋转前:[3,4,5,6,7,8,9];旋转后:[7,8,9,3,4,5,6]
- 双指针指向旋转后数组的首尾,作为区间端点。
- 若是区间中点值大于区间右界值,则最小的数字一定在中点右边。
- 若是区间中点值等于区间右界值,则是不容易分辨最小数字在哪半个区间,比如[1,1,1,0,1],应该逐个缩减右界。
- 若是区间中点值小于区间右界值,则最小的数字一定在中点或在中点左边。
- 通过调整区间最后即可锁定最小值所在。
代码
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
int left = 0;
int right = array.length - 1;
while(left < right){
int mid = (left + right) >> 1;
if(array[mid] > array[right]){//最小数字在mid右边
left = mid + 1;
}else if(array[mid] == array[right]){//无法判断,逐个缩减右界,一个个试
right--;
}else{//最小数字要么在mid要么在mid左边
right = mid;
}
}
return array[left];
}
}