二分总结
命名规则
为描述方便,下面文中提示的:
升序理解为不降序,即按顺序输入的数字,每个数字对比前面的数字,可等于可大于,但不能小于。
降序理解为不升序,即按顺序输入的数字,每个数字对比前面的数字,可等于可小于,但不能大于。
一、函数定义与用途
\(\large lower\_bound\)
用途:
升序的情况下,\(lower\_bound\)返回第一个 大于等于\(val\)的位置。
降序的情况下,\(lower\_bound\)返回第一个 小于等于\(val\)的位置。
\(\large upper\_bound\)
用途:
升序的情况下,\(upper\_bound\)返回第一个 大于\(val\)的位置。
降序的情况下,\(upper\_bound\)返回第一个 小于\(val\)的位置。
二、\(STL\)内置方法
\(\large lower\_bound\)
升序
int p = lower_bound(q, q + n, x) - q;
降序
int p = lower_bound(q, q + n, x, greater<int>()) - q;
\(\large upper\_bound\)
升序
int p = upper_bound(q, q + n, x) - q;
降序
int p = upper_bound(q, q + n, x, greater<int>()) - q;
二、手写左闭右开(推荐写法)
升序
int lower_bound(int q[], int l, int r, int x) {
while (l < r) {
int mid = (l + r) / 2;
if (q[mid] >= x)
r = mid;
else
l = mid + 1;
}
return l;
}
int upper_bound(int q[], int l, int r, int x) {
while (l < r) {
int mid = (l + r) / 2;
if (q[mid] > x)
r = mid;
else
l = mid + 1;
}
return l;
}
降序
int lower_bound(int q[], int l, int r, int x) {
while (l < r) {
int mid = (l + r) / 2;
if (q[mid] <= x)
r = mid;
else
l = mid + 1;
}
return l;
}
int upper_bound(int q[], int l, int r, int x) {
while (l < r) {
int mid = (l + r) / 2;
if (q[mid] < x)
r = mid;
else
l = mid + 1;
}
return l;
}
三、手写左闭右闭
升序
int lower_bound(int q[], int l, int r, int x) {
while (l <= r) {
int mid = (l + r) / 2;
if (q[mid] < x)
l = mid + 1;
else
r = mid - 1;
}
return l;
}
int upper_bound(int q[], int l, int r, int x) {
while (l <= r) {
int mid = (l + r) / 2;
if (q[mid] <= x)
l = mid + 1;
else
r = mid - 1;
}
return l;
}
降序
int lower_bound(int q[], int l, int r, int x) {
while (l <= r) {
int mid = (l + r) / 2;
if (q[mid] > x)
l = mid + 1;
else
r = mid - 1;
}
return l;
}
int upper_bound(int q[], int l, int r, int x) {
while (l <= r) {
int mid = (l + r) / 2;
if (q[mid] >= x)
l = mid + 1;
else
r = mid - 1;
}
return l;
}
四、升序测试完整代码
#include <bits/stdc++.h>
using namespace std;
const int N = 8;
int q[N] = {1, 2, 3, 4, 6, 7, 8, 9};
//本代码测试升序(不下降)的lower_bound和upper_bound情况
//前提是有序的情况下,lower_bound返回第一个大于等于x值的位置。(通过二分查找)
//左闭右开
int lower_bound1(int q[], int l, int r, int x) {
while (l < r) {
int mid = (l + r) / 2;
if (q[mid] >= x)
r = mid;
else
l = mid + 1;
}
return l;
}
int upper_bound1(int q[], int l, int r, int x) {
while (l < r) {
int mid = (l + r) / 2;
if (q[mid] > x)
r = mid;
else
l = mid + 1;
}
return l;
}
//左闭右闭
int lower_bound2(int q[], int l, int r, int x) {
while (l <= r) {
int mid = (l + r) / 2;
if (q[mid] < x)
l = mid + 1;
else
r = mid - 1;
}
return l;
}
int upper_bound2(int q[], int l, int r, int x) {
while (l <= r) {
int mid = (l + r) / 2;
if (q[mid] <= x)
l = mid + 1;
else
r = mid - 1;
}
return l;
}
int main() {
//在升序情况下,lower_bound理解为求大于等于x的第一个位置
// 1、数组中存在
printf("%d ", lower_bound1(q, 0, 8, 4));
printf("%d ", lower_bound2(q, 0, 7, 4));
printf("%d\n", int(lower_bound(q, q + 8, 4) - q));
// 2、数组中不存在
printf("%d ", lower_bound1(q, 0, 8, 5));
printf("%d ", lower_bound2(q, 0, 7, 5));
printf("%d\n", int(lower_bound(q, q + 8, 5) - q));
// 3、数组左侧
printf("%d ", lower_bound1(q, 0, 8, 0));
printf("%d ", lower_bound2(q, 0, 7, 0));
printf("%d\n", int(lower_bound(q, q + 8, 0) - q));
// 4、数组右侧
printf("%d ", lower_bound1(q, 0, 8, 10));
printf("%d ", lower_bound2(q, 0, 7, 10));
printf("%d\n", int(lower_bound(q, q + 8, 10) - q));
puts("==================================");
//在升序情况下,upper_bound理解为求大于x的第一个位置
// 1、数组中存在
printf("%d ", upper_bound1(q, 0, 8, 4));
printf("%d ", upper_bound2(q, 0, 7, 4));
printf("%d\n", int(upper_bound(q, q + 8, 4) - q));
// 2、数组中不存在
printf("%d ", upper_bound1(q, 0, 8, 5));
printf("%d ", upper_bound2(q, 0, 7, 5));
printf("%d\n", int(upper_bound(q, q + 8, 5) - q));
// 3、数组左侧
printf("%d ", upper_bound1(q, 0, 8, 0));
printf("%d ", upper_bound2(q, 0, 7, 0));
printf("%d\n", int(upper_bound(q, q + 8, 0) - q));
// 4、数组右侧
printf("%d ", upper_bound1(q, 0, 8, 10));
printf("%d ", upper_bound2(q, 0, 7, 10));
printf("%d\n", int(upper_bound(q, q + 8, 10) - q));
return 0;
}
五、降序测试完整代码
#include <bits/stdc++.h>
using namespace std;
const int n = 8;
int q[n] = {9, 8, 7, 6, 4, 3, 2, 1};
//本代码测试降序(不上升)的lower_bound和upper_bound情况
//左闭右开
int lower_bound1(int q[], int l, int r, int x) {
while (l < r) {
int mid = (l + r) / 2;
if (q[mid] <= x)
r = mid;
else
l = mid + 1;
}
return l;
}
int upper_bound1(int q[], int l, int r, int x) {
while (l < r) {
int mid = (l + r) / 2;
if (q[mid] < x)
r = mid;
else
l = mid + 1;
}
return l;
}
//左闭右闭
int lower_bound2(int q[], int l, int r, int x) {
while (l <= r) {
int mid = (l + r) / 2;
if (q[mid] > x)
l = mid + 1;
else
r = mid - 1;
}
return l;
}
int upper_bound2(int q[], int l, int r, int x) {
while (l <= r) {
int mid = (l + r) / 2;
if (q[mid] >= x)
l = mid + 1;
else
r = mid - 1;
}
return l;
}
int main() {
//在降序情况下,lower_bound理解为求小于等于x的第一个位置
// 1、数组中存在
printf("%lld ", lower_bound(q, q + n, 4, greater<int>()) - q); //返回第一次出现的位置
printf("%d ", lower_bound1(q, 0, n, 4));
printf("%d \n", lower_bound2(q, 0, n - 1, 4));
// 2、数组中不存在
printf("%lld ", lower_bound(q, q + n, 5, greater<int>()) - q); //返回第一个插入x不影响原序列顺序的位置
printf("%d ", lower_bound1(q, 0, n, 5));
printf("%d \n", lower_bound2(q, 0, n - 1, 5));
// 3、数组左侧
printf("%lld ", lower_bound(q, q + n, 10, greater<int>()) - q); //返回0
printf("%d ", lower_bound1(q, 0, n, 10));
printf("%d\n", lower_bound2(q, 0, n - 1, 10));
// 4、数组右侧
printf("%lld ", lower_bound(q, q + n, 0, greater<int>()) - q); //返回最后一个元素下标+1
printf("%d ", lower_bound1(q, 0, n, 0));
printf("%d\n", lower_bound2(q, 0, n - 1, 0));
puts("==================================");
//在降序情况下,upper_bound理解为求小于x的第一个位置
// 1、数组中存在
printf("%lld ", upper_bound(q, q + n, 4, greater<int>()) - q); //返回第一次出现的位置
printf("%d ", upper_bound1(q, 0, n, 4));
printf("%d\n", upper_bound2(q, 0, n - 1, 4));
// 2、数组中不存在
printf("%lld ", upper_bound(q, q + n, 5, greater<int>()) - q); //返回第一个插入x不影响原序列顺序的位置
printf("%d ", upper_bound1(q, 0, n, 5));
printf("%d\n", upper_bound2(q, 0, n - 1, 5));
// 3、数组左侧
printf("%lld ", upper_bound(q, q + n, 10, greater<int>()) - q); //返回0
printf("%d ", upper_bound1(q, 0, n, 10));
printf("%d\n", upper_bound2(q, 0, n - 1, 10));
// 4、数组右侧
printf("%lld ", upper_bound(q, q + n, 0, greater<int>()) - q); //返回最后一个元素下标+1
printf("%d ", upper_bound1(q, 0, n, 0));
printf("%d\n", upper_bound2(q, 0, n - 1, 0));
return 0;
}
六、经验总结
-
二分模板在网上非常多,在边界处理上有各种各样的处理方式,感受后,认为\(STL\)的思路是最好的:左闭右开
-
一般来讲,\(STL\)可以处理大于等于,大于,小于等于,小于,就基本够用了,原则上能用\(STL\)的二分办法解决的,尽量用\(STL\),实在不行的,使用手写的左闭右开区间办法。(
挖坑待填,啥样是实在不行呢?) -
查找左边界,可以直接\(lower\_bound\),如果想要查找右边界,可以使用\(upper\_bound\)然后再减\(1\)。