关于二分查找的思考

114 阅读3分钟

二分查找是算法中常用的思想,但是怎么二分又内有乾坤

二分核心思想

「二分」的本质是两段性,并非单调性。只要一段满足某个性质,另外一段不满足某个性质,就可以用「二分」。

二分解法模板

两个模板,不是用哪个都行,需要分析题意。

  • 如果是最简单的二分,查找值,那么用哪个无所谓。(一般都不会)
  • 如果是两次二分,或者是需要找出边界值。就得看你需要左边还是右边来动态选择模板了

模板1

当我们将区间[l,r]划分成[l,mid]和[mid+1,r]时,其更新操作是r=mid或者l=mid+1,计算mid时不需要加1,即mid=(l+r)/2

long l = 0, r = 1000009;
while (l < r) {
    long mid = l + r >> 1;
    if (check(mid)) { //条件
        r = mid;
    } else {
        l = mid + 1;
    }
}

int bsearch_1(int x){
    int l = 0,r = n - 1;
    while(l < r){
        int mid = l + r >> 1;
        if(q[mid] >= x) r = mid;
        else l = mid + 1;
    }
    
    return l;
}



用这个模板的就表示最后的left和right都是偏左方的

模板2

当我们将区间[l,r]划分成[l,mid−1]和[mid,r]时,其更新操作是r=mid−1或者l=mid,此时为了防止死循环,计算mid时需要加1,即mid=(l+r+1)/2。 偏右方

    int l = 0, r = n - 1;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (nums[mid] <= nums[0]) { //这里是你的条件
                l = mid;
            } else {
                r = mid - 1;
            }
        }
        
 
 int bsearch_2(int x){
    int l = 0, r = n - 1;
    while(l < r){
        int mid = l + r + 1 >> 1;
        if(q[mid] <= x) l = mid;
        else r = mid - 1;
    }
    
    return l;
}

        

总结

简单的总结:当需要找分界点的时候,使用l=mid-1的,主要就是防止分界点是最后一个

这题便就是只能使用缩短左边界

153. 寻找旋转排序数组中的最小值

已知一个长度为 n 的数组,预先按照升序排列,经由 1n旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], ..., a[n-1]]旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。

示例 2:

输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 3 次得到输入数组。

示例 3:

输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。