推荐使用的二分查找模板

概述

本文和前面标准二分查找模板,https://blog.csdn.net/justidle/article/details/104303389,有比较大的不同,在写二分查找的时候,建议使用本文的方式。

二分查找本质

如下图所示,我们将一个 [l, r] 区分分为两个区域。只要我们能将一个区域分为两步部分,那么我们即可以查找红色的边界点,也可以查找绿色的边界点。二分绿色或者红色点,就是我们两个不同模板。

二分要素

一个 check 函数

函数原型为 bool check(int x),该函数用于检查 x 是否满足某种属性。

bool check(int x) {
    ......
}

三个变量

1、左边界(left)。用于描述查找区间的左边界。

2、右边界(right)。用于描述查找区间的右边界。

3、中间值(mid)。通过左边界和右边界计算而得。在二分查找中,使用这个 mid 数值验证是否满足题目的条件,也就是调用 check(mid)。

循环条件

当 left < right 成立的时候,进行二分查找。

区间缩小

1、当 check() 函数成立的时候,缩小边界,即 right = mid 或者 left = mid。

2、当 check() 函数不成立的时候,缩小边界,即 left = mid+1 或者 right = mid-1。

具体使用哪种模板在下面讨论。

返回值

左边界。即 left 为二分查找的结果。

模板代码

模板一

二分出第一个图的红色点。我们将 [l, r] 这个区域划分为 [l, mid-1], [mid, r] 这两个区域。

我们知道二分的核心是以 mid 为边界,判断中间值是否满足题目的某种性质,本模板是 check(mid) 这个函数满足红色区域的性质。假设 check() 满足红色区域性质,那么 mid 一定是在红色区域里,如下图所示,那么答案自然在 [mid, r] 这个区域,区域更新方式就是  l=mid。

假设 check() 不满足红色区域性质,那么 mid 一定是在绿色区域里,如下图所示,那么答案自然在 [l, mid-1] 这个区域,区域更新方式就是  r=mid-1。

参考代码如下:

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

注意:mid 的计算方法和模板一是不相同的。

模板二

二分出第一个图的绿色点。我们将 [l, r] 这个区域划分为 [l, mid], [mid+1, r] 这两个区域。

我们知道二分的核心是以 mid 为边界,判断中间值是否满足题目的某种性质,本模板是 check(mid) 这个函数满足绿色区域的性质。假设 check() 满足绿色区域性质,那么 mid 一定是在绿色区域里,如下图所示,那么答案自然 [l, mid] 这个区域,区域更新方式就是  r=mid。

假设 check() 不满足绿色区域性质,那么 mid 一定是在红色区域里,如下图所示,那么答案自然在 [mid+1, r] 这个区域,区域更新方式就是  l=mid+1。

参考代码如下:

int bsearch_1(int l, int r) {
    while (l < r) {
        int mid = l+((r-l)>>1);
        if (check(mid)) {
            r = mid;    // check()判断mid是否满足性质
        } else {
            l = mid + 1;
        }
    }
    return l;
}

口诀

1、先写 check() 函数。

2、在随便套用一种二分查找模板。

3、确定使用哪种区间划分方案,修改代码。

发布了203 篇原创文章 · 获赞 101 · 访问量 104万+

猜你喜欢

转载自blog.csdn.net/justidle/article/details/104527596
今日推荐