题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
解法分析
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
if(array == null || array.length == 0)
return 0;
int p1 = 0, p2 = array.length-1;
//也可以使用二分查找
//p1指向前面的排序数组,p2指向后面的排序数组
//如果数组本身就有序(旋转0个元素),直接返回mid的值
int mid = p1;
while(p1 < p2 && array[p1] >= array[p2]) {
if(p2 - p1 == 1) {
mid = p2;
break;
}
mid = p1 + (p2 - p1) / 2;
//特殊情况:p1, mid, p2三者相等
if(array[p1] == array[mid] && array[p2] == array[mid]) {
//顺序查找
int res = array[p1];
for(int i=p1+1; i<=p2; i++) {
res = Math.min(res, array[i]);
}
return res;
}
//判断当前mid是否位于前面的排序数组中
if(array[mid] >= array[p1]){
p1 = mid;
} else if(array[mid] <= array[p2]) {
p2 = mid;
}
}
return array[mid];
}
}
解法一为直接遍历数组找最小值,虽然复杂度为O(n),但并没有利用题目中所说的排序数组的条件(虽然是旋转的排序数组).想到排序数组的查找应该就是二分查找了,这道题是变种的二分查找。
具体解法为:
1)两个指针p1, p2分别指向数组的起始和结束位置(和普通的二分查找一样)
2)中间元素:这里需要考虑如何根据和中间元素的比较来缩小查找的范围,普通的二分查找直接比较就好了。观察这里的数组,排序数组被分为了两部分:前半部分(旋转的)和后半部分,若中间元素mid大于等于p1,则mid一定在前半部分的排序数组,这时可以将p1移到mid的位置:p1=mid
。同理,若mid小于等于p2,则p2=mid
。最后p1指向的是前半部分的最后一个元素,p2指向的是后半部分的第一个元素(即最小元素)
例子:3 4 5 1 2
需要注意的特殊情况:
1)数组可能旋转了0个元素(即没有旋转)
2)p1、mid、p2三者指向的元素相等,即无法判断中间的数字是位于前面的子数组还是后面的子数组,从而无法移动指针缩小查找范围。这时不得不采用顺序查找的方法。
例子:1 0 1 1 1
和1 1 1 0 1
(加阴影的为旋转的)
参考剑指offer题解