0
点赞
收藏
分享

微信扫一扫

二分总结

陬者 2022-07-18 阅读 94

二分总结

命名规则
为描述方便,下面文中提示的:

升序理解为不降序,即按顺序输入的数字,每个数字对比前面的数字,可等于可大于,但不能小于。

降序理解为不升序,即按顺序输入的数字,每个数字对比前面的数字,可等于可小于,但不能大于。

一、函数定义与用途

\(\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\)。



举报

相关推荐

0 条评论