题目描述:
思路分析:
这道题最直观的解法并不难,从头到尾遍历数组一次即可找到最小元素,时间复杂度为O(n),但是这个思路并没有利用到“旋转数组”特性,继续思考。注意到旋转之后的数组实际上可以划分为两个排序子数组,而且前面的子数组元素都大于或等于后面子数组的元素。还可以注意到最小的元素刚好是这两个子数组的分界点。在排序数组中我们可以用二分查找法实现O(logn)的查找。
同二分查找法一样,我们用两个指针分别指向数组的第一个元素和最后一个元素。按照旋转规则,第一个元素应该大于等于最后一个元素。接着可以找到数组中间的元素。如果中间元素位于前面的递增数组,则它应该大于等于第一个指针指向的元素,此时我们要找到的最小元素肯定应该位于该中间元素的后面。这样我们把第一个指针指向中间元素,从而缩小查找范围。注意,移动之后的第一个指针仍然是位于前面的递增数组中。
同理,如果中间元素位于后面的递增子数组,那么它应该小于等于第二个指针指向的元素。此时最小元素应该位于中间元素的前面或者就是此中间元素。我们把第二个指针指向中间元素,从而缩小查找范围。注意,移动之后的第二个指针仍然是位于后面的递增数组中。
按照上述思路,第一个指针总是指向前面的递增数组元素,而第二个指针总是指向后面的递增数组元素。最终第一个指针将指向前面的子数组的最后一个元素,第二个指针将指向后面的子数组的第一个元素,即二者最后指向相邻的元素,且第二个指针指向的刚好是最小的元素——循环结束条件。
注意:
1)如果把排序数组的前面的0个元素搬到最后面,即排序数组本身,这仍是一个旋转数组,此时,数组中的第一个数字就是最小数字,可以直接返回,所以把中间元素mid初始化为0。
2)如果第一个指针指向的元素、第二个指针指向的元素和中间元素相同,则无法判断中间元素是属于哪个子数组,也就无法移动两个指针来缩小查找范围,此时还是要用顺序查找的方法。
解决:
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
if(array == null)
return 0;
int i = 0, j = array.length - 1, mid = 0;
while(array[i] >= array[j]){
if(j-i==1){
mid = j;
break;
}
mid = (i+j)/2;
//如果array[i]==array[j]==array[mid]
//则不能用二分查找,因为不确定array[mid]位于哪个有序子数组
//只能用顺序查找
if(array[i]==array[j]&&array[j]==array[mid]){
//在除了Main()方法中调用其他方法需要new实例之外,其他方法中调用同一个类中的方法直接用:方法名(实参)即可,不需要其他操作
return findInOrder(array,i,j);
}
if(array[i]<=array[mid])
i=mid;
else if(array[mid]<=array[j])
j=mid;
}
return array[mid];
}
//顺序查找
public int findInOrder(int[] a, int in1, int in2){
int k = a[in1];
for(int i = in1+1; i < in2; i++)
if(a[i]<k)
k=a[i];
return k;
}
}