【剑指offer】11.旋转数组的最小数字

在这里插入图片描述
采用二分法解答这个问题,
mid = l + r>>1;
需要考虑三种情况:
(1)array[mid] > array[r]:
出现这种情况的array类似[3,4,5,6,0,1,2],此时最小数字一定在mid的右边。
low = mid + 1
(2)array[mid] == array[r]:
出现这种情况的array类似 [1,0,1,1,1] 或者[1,1,1,0,1],此时最小数字不好判断在mid左边
还是右边,这时只好一个一个试 ,
– r;
(3)array[mid] < array[high]:
出现这种情况的array类似[2,2,3,4,5,6],此时最小数字一定就是array[mid]或者在mid的左
边。因为右边必然都是递增的。
r = mid
注意这里有个坑:如果待查询的范围最后只剩两个数,那么mid 一定会指向下标靠前的数字
比如 array = [2,3]
array[ l ] = 2 ;array[ mid] = 2 ; array[r] = 3 ;
如果 r = mid - 1,就会产生错误, 因此r = mid
但情形(1)中low = mid + 1就不会错误

java:

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        int l = 0;
        int r = array.length -1;
        int mid=0;
        while(l<r){
            mid = l+r>>1;
            if(array[mid]>array[r])
                l = mid +1;
            else if(array[mid]==array[r])
                --r;
               else
                   r = mid;
        }
        return array[ l ];
        //return array [ r ];  也行 最后跳出 l<r 是条件 就算 l=r
    }
}

二分:

中点mid属于左半区间,则左半区间是[l, mid],右半区间是[mid+1, r],更新方式是r = mid;或者 l = mid + 1;,此时用第一个模板:
版本1
当我们将区间[l, r]划分成[l, mid]和[mid + 1, r]时,其更新操作是r = mid或者l = mid + 1;,计算mid时不需要加1。

int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    return l;
}

版本2
中点mid属于右半区间,则左半区间是[l, mid-1],右半区间是[mid, r],更新方式是r = mid - 1;或者 l = mid;,此时用第二个模板:
当我们将区间[l, r]划分成[l, mid - 1]和[mid, r]时,其更新操作是r = mid - 1或者l = mid;,此时为了防止死循环,计算mid时需要加1。

不写+1的话,当 l = r - 1的时候就会死循环。
任何时候都不会写成 while (l < r)

int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

猜你喜欢

转载自blog.csdn.net/beauman/article/details/89552325