地址
题目
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
思路
1.旋转数组可以看做两个非减排序数组,第一个数组中的元素必定大于或等于第二个数组中的元素
2.题目中的元素最小值为这两个数组的分界点。所以这里设定两个指针,一个指向数组首位(low),另一个指向数组末尾(high),使用二分查找缩小查找范围,当high-low == 1时,表示已经到达两个数组的分界点,因此这个作为循环的停止条件。
3.当首元素等于尾元素等于中间元素时,最小值有可能出现在左右两侧,如{1,0,1,1,1}或{1,1,1,0,1},所以无法使用二分查找,只能使用遍历顺序查询。
4.如果中间元素(mid)大于首位元素,则中间元素一定位于第一个数组中,则两个数组的分界点在中间元素的右边,所以low = mid。如{3,4,5,1,2}
5.如果中间元素小于首位元素,则中间元素一定位于第二个数组中,则分界点在中间元素的左边,所以high = mid。如{4,5,1,2,3}
时间复杂度为:O(nlogn)
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
//数组为0
if(array.length == 0) return 0;
//二分查找
int low = 0;
int high = array.length - 1;
int mid = 0;
//旋转数组的特性:
//1,最后一个元素小于等于第一个元素
//保证是旋转数组
while(array[low] >= array[high]){
//循环停止条件
if(high - low == 1){
mid = high;
break;
}
//求中间元素
mid = low + (high-low)/2;
//1.第一种情况:如果头尾和中间元素相等,则无法进行二分,需要顺序执行
if(array[low] == array[mid] && array[low] == array[high]){
return returnMin(array,low,high);
}
//2.第二种情况
//如果中间元素大于等于第一个元素,则表明mid在第一个数组,最小值在后面一个数组
if(array[mid] >= array[low] ){
//缩小范围
low = mid;
}else{
//如果中间元素小于第二个元素,则表示mid在第二个数组,最小元素在mid的前面
high = mid;
}
}
return array[mid];
}
private int returnMin(int[] a,int low,int high){
int min = a[low];
for(int i = low+1;i <= high;i++){
if(min > a[i])
min = a[i];
}
return min;
}
}
当然按照题目可以直接遍历,如果是旋转数组,前一个元素大于后一个元素,则后一个元素为最小值,否则就没有进行旋转,直接返回第一个元素,时间复杂度:O(n)。这里不推荐使用这种方法,因为该题目想考察的就是二分查找的应用。
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
int n = array.length;
if(n == 0) return 0;
for(int i = 0;i < n - 1;i++){
if(array[i] > array[i+1])
return array[i+1];
}
return array[0];
}
}