题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
方法一
一、分析
下图中上面的曲线可以看做原数组,从竖线处切分后,得到下面的图。我们的目标就是找那个最低点
计算数组是排序的,再线性地查找就显得不高效了。排序数组,自然要想到二分查找。只是这里的操作和常规二分查找不同。
二分查找的 mid
如果落在左半边,那么就 lo=mid+1
,如果落在右半边,就减小 hi=mid
。不断缩小范围,最终,lo
一定会掉到最低点。
但是要如何判断 mid
落在那一边呢。因为左半边 >= 右半边,如果原数组是严格递增的,你可以说,当 a[mid] > a[0]
那就是左半边。但是考虑下面这幅图:
如果原数组只有开始部分递增,之后很长一段都是平坦的,那么旋转后可能是上图的样子。a[mid] == a[lo] == a[hi]
,根本没法判断 mid 在那边。解决的方法很简单
while(a[lo] == a[hi]){
++lo;
--hi;
}
这样操作以后,一定有 a[lo] > a[hi]
,一旦 lo
掉到了最低点,这个条件就不成立了。判断 mid 在左在右也很简单了,如果 a[mid] >= a[lo]
那就是左边。如果 a[mid] <= a[hi]
那就是右边。
如果在左边,那就 lo=mid+1
,这样把 lo 向右边推进。如果在右边,为了避免 hi
爬到了左边去 hi=mid
即可。最终终止条件,就是 lo
落到了最低点。
二、代码1
class Solution {
public:
int minNumberInRotateArray(vector<int> a) {
if (a.size() == 0) return 0;
int lo = 0, hi = a.size()-1;
while(a[lo] == a[hi]){
++lo;
--hi;
}
while(a[lo] > a[hi]){
int mid = lo + (hi-lo) / 2;
if(a[mid] >= a[lo]){
lo = mid + 1;
}else if(a[mid] <= a[hi]){
hi = mid;
}
}
return a[lo];
}
};
三、代码2
public static int minNumberInRotateArray(int[] array) {
if (array.length == 0)
return 0;
int left = 0;
int right = array.length - 1;
int middle = -1;
while (array[left]>=array[right]) {
if(right-left==1){
middle = right;
break;
}
middle = left + (right - left) / 2;
if (array[middle] >= array[left]) {
left = middle;
}
if (array[middle] <= array[right]) {
right = middle;
}
}
return array[middle];
}
方法二
一、分析
二、代码
public int minNumberInRotateArray(int[] array) {
if (array.length == 0)
return 0;
for (int i = 0; i < array.length - 1; i++) {
if (array[i] > array[i + 1])
return array[i + 1];
}
return array[0];
}