《剑指Offer》Java刷题 NO.6 旋转数组的最小数字(数组、二分法变式)
传送门:《剑指Offer刷题总目录》
时间:2020-02-09
题目:
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
这道题在网上找了很多个代码,自己也写了,但是就算是能在牛客上跑通,也不能跑通自己试的一些例子,比如:
int []a1={2,2,2,2,2,2,1,1,1,2};
int []a2={1,1,1,1,1,1,1};
后来找到大神的一份代码,简洁又完美
思路:(参考大神FIANCK)
1. 利用二分法的思想,降低算法复杂度
2. 利用中间元素和尾部元素相比较(比和头部相比较简单一些),无非就是三种情况:
1.array[mid]>array[right]:
此时mid一定在最小值之前,操作:left=mid+1;
2.array[mid]==array[right]:
此时无法判断mid在最小值之前还是之后,为了避免跳过最小点,应该:right--;
3.array[mid]<array[right]:
此时mid一定在最小值之后或者就是最小值,为了避免跳过最小点,应该:right=mid;
这样一旦left移动到右边就一定是处于最小值处:
1.等着right一点一点向左移动至和left相等时,将array[left]返回;
2.或者更常见的情况,一旦发现array[left]<array[right],就可以把array[left]返回了,不用等right移动到left处
3. 总体复杂度为O)(logN),某些特殊的情况会退化至O(N)[数组全是相等数字的情况]
Java代码:
/**
* 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
* 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
* 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
* NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
* 特例:空数组、相等数组、非递增数组(eg:{5,5,5,5,5,2,2,2,5,5}),升序数组,单个值数组
*/
public class MinInRotateArray {
public int minNumberInRotateArray(int[] array) {
if(array.length==0) return 0;
int left=0;
int right=array.length-1;//数组左右边界
int mid;
while(left<right){
//升序数组
if (array[left] < array[right])
return array[left];
mid=left+(right-left)/2;
if(array[mid]>array[right])//mid在最小值左边
left=mid+1;//必须+1,否则最后只剩两个数而且最小值在最后的时候,会陷入死循环
else if(array[mid]==array[right])//不能判断mid在左边还是右边
right--;//为了不跳过最小值,一点点试探
else {right=mid;}//mid在最小值右边或者就是最小值,为了避免跳过最小值,此处不能-1
}
return array[left];//left一旦到了右边就会在最小值处,而且不会再移动,等着right移动到
//和left相等的地方就把该处的值返回就可以了
}
public int minNumberOfIndexInRotateArray(int[] array) {
if(array.length==0) return 0;
int left=0;
int right=array.length-1;//数组左右边界
int mid;
while(left<right){
if (array[left] < array[right])
return array[left];
mid=left+(right-left)/2;
if(array[mid]>array[right])//mid在最小值左边
left=mid+1;//必须+1,否则最后只剩两个数而且最小值在最后的时候,会陷入死循环
else if(array[mid]==array[right])//不能判断mid在左边还是右边
right--;//为了不跳过最小值,一点点试探
else {right=mid;}//mid在最小值右边或者就是最小值,为了避免跳过最小值,此处不能-1
}
return left;//left一旦到了右边就会在最小值处,而且不会再移动,等着right移动到
//和left相等的地方就把该处的值返回就可以了
}
public static void main(String[] args) {
int []a1={2,2,2,2,2,2,1,1,1,2};
int []a2={1,1,1,1,1,1,1};
int []a3={0,1};
MinInRotateArray min=new MinInRotateArray();
System.out.println(min.minNumberInRotateArray(a1));
System.out.println(min.minNumberOfIndexInRotateArray(a1));
System.out.println(min.minNumberInRotateArray(a2));
System.out.println(min.minNumberOfIndexInRotateArray(a2));
System.out.println(min.minNumberInRotateArray(a3));
}
}