二分查找:
• 在指定的区间,尝试中间值。 • 如果中间值就是答案则输出答案。 • 如果中间值太小,则继续处理右区间。 • 如果中间值太大,则继续处理左区间。 • 每次都可以把可能的数据缩小一半。
这种每次淘汰掉一半区间,最后只留下一个的做法,是二分查找。
整数二分模板:
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, 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;
}
浮点数二分模板:
bool check(double x) {/* ... */} // 检查x是否满足某种性质
double bsearch_3(double l, double r)
{
const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求
while (r - l > eps)
{
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
其实在STL里面也有已经实现好的二分函数,包含于algorithm库中。
lower_bound(begin,end,val):在有序数组 [begin,end) 中找到第 一个大于等于 val 的值并返回其地址。
upper_bound(begin,end,val):在有序数组[begin,end)中找到第 一个大于 val 的值并返回其地址。 和大多数 STL 函数一样,begin 指数组首地址,end 指末地址+1。
需要注意的是,如果在数组中返回得到的结果将会是一个地址,而我们经常需要将其转化为下标。
例如下面的代码得到的就是 a 数组(下标从0开始)第一个大于等 于 val 的值的下标。
int pos = lower_bound(a, a+n, val) - a;
二分答案:
二分思想不仅可以在有序序列中快速查询元素,还能高效率地解 决一些具有单调性判定的问题。 事实上,二分查找的本质,就是在寻找一个满足“a[mid]>=k”这一 条件的最小的 mid。由于 a 数组的单调性,我们设计出了这一算 法。
事实上,在一些具有单调性判定的问题中,我们也可以采用类似 方法求解。
使用二分答案的条件: 1. 命题可以被归纳为找到使得某命题P(x)成立(或不成立)的最 大(或最小)的x。 2. 把P(x)看做一个值为真或假的函数,那么它一定在某个分界线 的一侧全为真,另一侧全为假。 3. 可以找到一个复杂度优秀的算法来检验P(x)的真假。通俗来讲,二分答案可以用来处理“最大的最小”或“最小的最大”问 题。