整数二分

898 字
4 分钟
整数二分

整数二分#

整数二分最容易让人混乱的地方不是循环,而是边界。很多题不是直接问“某个数在哪里”,而是问:

  • 第一个满足条件的位置在哪里。
  • 最后一个满足条件的位置在哪里。

这两种问题对应两套模板。

1、什么时候可以二分#

只要答案区间具有单调性,就可以考虑二分。

比如一个数组满足:

小于 x 的部分 | 大于等于 x 的部分

那么“第一个大于等于 x 的位置”就可以二分。

再比如:

满足条件的部分 | 不满足条件的部分

或者反过来,只要能被某个 check(mid) 分成两段,就能二分。

2、模板一:找左边界#

当区间被分成:

[l, mid] 和 [mid + 1, r]

通常是在找第一个满足条件的位置。

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

这里 check(mid) 表示:答案可能在 mid 或 mid 左边。

3、模板二:找右边界#

当区间被分成:

[l, mid - 1] 和 [mid, r]

通常是在找最后一个满足条件的位置。

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

这里 mid 要写成:

int mid = (l + r + 1) >> 1;

原因是当 l + 1 == r 时,如果还用 (l + r) >> 1,mid 会等于 l,可能导致死循环。

4、例子:有序数组中查找 x 的范围#

给定有序数组:

1 2 2 2 3 4

查找 2 第一次和最后一次出现的位置。

左边界是第一个 >= 2 的位置。

右边界是最后一个 <= 2 的位置。

5、完整代码#

#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, q;
int a[N];
int x;
bool check_left(int mid) {
return a[mid] >= x;
}
bool check_right(int mid) {
return a[mid] <= x;
}
int bsearch_left(int l, int r) {
while (l < r) {
int mid = (l + r) >> 1;
if (check_left(mid)) r = mid;
else l = mid + 1;
}
return l;
}
int bsearch_right(int l, int r) {
while (l < r) {
int mid = (l + r + 1) >> 1;
if (check_right(mid)) l = mid;
else r = mid - 1;
}
return l;
}
int main() {
cin >> n >> q;
for (int i = 0; i < n; i++) cin >> a[i];
while (q--) {
cin >> x;
int l = bsearch_left(0, n - 1);
if (a[l] != x) {
cout << "-1 -1" << endl;
} else {
int r = bsearch_right(0, n - 1);
cout << l << " " << r << endl;
}
}
return 0;
}

6、如何判断用哪个模板#

可以先问一句:

答案更像是左边界,还是右边界?

如果是找“第一个满足条件”,用左边界模板。

如果是找“最后一个满足条件”,用右边界模板。

7、容易写错的地方#

(1)右边界模板忘记 +1#

int mid = (l + r + 1) >> 1;

这是防止死循环的关键。

(2)check 的含义不清楚#

写二分前先用一句话定义 check(mid)。

比如:

check(mid):a[mid] 是否大于等于 x

定义清楚后,更新边界就不容易乱。

(3)没有检查答案是否存在#

二分只能找到边界,不能保证目标值一定存在。所以找到位置后还要判断:

if (a[l] != x)

总结#

整数二分不要硬背一堆变形。先记住两套模板:

找左边界:mid = (l + r) >> 1
找右边界:mid = (l + r + 1) >> 1

然后把 check(mid) 的含义写清楚,边界更新就会自然很多。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

整数二分
https://blog.tjcheng.cn/posts/migrated/old-76/
作者
TengJinCheng
发布于
2025-10-01
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
TengJinCheng
Hello, I'm Teng.
公告
欢迎来到TengJinCheng工作铺!
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
分类
标签
站点统计
文章
157
分类
7
标签
114
总字数
109,939
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.13.4
文章许可
CC BY-NC-SA 4.0

文章目录