牛客网刷题剑指offer

[查找和排序]旋转数组的最小数字

2018-11-01  本文已影响0人  闭门造折

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

以自然数举例

num[]:第一行表示索引,下方表示对应的值(上下位置表示的是数字大小关系)
0 1 2 3 4 5 6 7 8
| | | | | | | | 9
| | | | | | | 8
| | | | | | 7
| | | | | 6
| | | | 5
| | | 4
| | 3
| 2
1  

随意旋转一下数组,可能会得到这样的结果

num[]:第一行表示索引,下方表示对应的值(上下位置表示的是数字大小关系)
0 1 2 3 4 5 6 7 8
| | | 9 | | | | |
| | 8   | | | | |
| 7     | | | | |
6       | | | | |
        | | | | 5
        | | | 4
        | | 3
        | 2
        1

假设原数组是严格单增的(后续会提到若存在不严格单增如何处理)
那么旋转后数组应该就像事例中结果,存在两个单增的子序列。
假设一个数字x,x ≥ num[0],那么说明x在第一段子序列中
假设x ≤ num[n - 1],那么说明x在第二段子序列中

采用二分查找的方法,首先二分的两头为[0,n-1]
找到中间值mid,mid作为索引所对应的值num[mid]假如在第一段子序列中,说明左指针仍然有右移空间。反之说明右指针有左移空间
二分找到值骤减的连续两项,后一项即为所求的最小数字

这道题是不严格单增的,即可能出现等值情况。这个时候我们没有办法,只能用暴力法寻找最小值。一个例子为

索引     0 1 2 3 4 5 6 7 8 9
数组     1 1 1 0 1 1 1 1 1 1
一次二分          1 1 1 1 1 1

显然二分时丢失了部分可能的最小值

具体代码:

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array.length == 0){
            return 0;//因传入参数而导致题目无意义
        }
        /*初值设定
        * left : 数组起始索引 0
        * right: 数组尾部索引 n - 1
        * mid  : 中间值索引
        */
        int left = 0;
        int right = array.length - 1;
        int mid = (left + right ) / 2;
        
        //只有left值 > right值,二分才有意义
        //等于时,需要暴力查找
        while(array[left] > array[right]){
            //假设left和right已经只相差1了,说明已经找到了。
            //此时left指向最大值,right指向最小值
            //由于while条件不取等,所以不会出现left = right情况
            if(left == right - 1){
                mid = right;
                break;
            }
            //找到中间索引值
            mid = (left + right) / 2;
            //mid在第一段非减区间
            if(array[left] <= array[mid]){
                left = mid;
            }
            //mid在第二段非减区间
            else{
                right = mid;
            }
        }
        //左右侧相等时,暴力搜索
        if(array[left] == array[right]){
            for(int i = left; i < right; i++){
                //mid锁定最小array值位置
                mid = (array[mid] < array[i])? mid : i;
            }
        }
        return array[mid];
    }
}
上一篇下一篇

猜你喜欢

热点阅读