概述
本文和前面标准二分查找模板,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、确定使用哪种区间划分方案,修改代码。