目录
二分的本质:二分就是找到一个性质,可以将一组数据一分为二,使得一边是满足的,另一边是不满足的,二分就是用来寻找这个性质的边界,并且保证性质在区间之内
整数二分就是对整数进行二分,需要小心对应找中间值的处理,小心陷入死循环
二分可以解决单调性的问题,但是没有单调性也不是不可以二分
L r
|———————|———————|
第一种情况:找左边的性质的边界
Step1⭐. 确定分界点:找中间值 mid = (l+r+1)/2 ,然后检查一下这个分界点是否满足左边的性质
Step2、调整区间:如果mid满足,则用 [mid,r], 使 l=mid;
如果不满足,用[l,mid-1], 使 r=mid-1
第二种情况:找右边的性质的边界
Step1⭐、确定分界点:找中间值 mid = (l+r )/2,然后检查一下这个分界点是否满足左边的性质
Step2、调整区间:如果mid满足,则用 [l, mid],r=mid;
如果不满足,用 [mid+1,r],l=mid+1;
//为什么第一种情况中需要补上+1?
例如说,当l = r-1的时候,mid如果只是(l+r)/2,就还是l,如果mid满足条件,更新l=mid,l就还是原来的l,会陷入死循环,所以要加1,让mid算出来是r
//如何判断,更新中间值的时候,需不需要加一⭐
可以在写代码的时候,先不加1,然后 在调整区间的时候,看一下更新的时候,如果 r=mid-1 有减一的操作,就再去中间值更新的地方加上1,如果更新区间的时候,mid+1 或者直接取mid 没有加减操作的,就都可以不用补加一。
Step3、一直到最后,l 会和r 相遇,判断一下l 是不是我们要找的,就完成了。
Acwing 789题 数的范围 链接: AcWing 789. 数的范围 - AcWing
像这种查找问题,特别是查找边界的问题,感觉 都会用二分,
这个题的题目描述和解题代码如下:
- /*
- 这个题是需要求输入的数,在数组中的起始位置和结束位置
- 像这种求边界的问题就 很适合用 二分查找 来解决问题
- */
-
- #include
- using namespace std; //前两行 不练就会忘记了,嘤嘤嘤
-
- const int N = 100010;
-
- int q[N];
-
- int main(){
- int n, m;
- scanf("%d%d", &n, &m);
-
- for(int i = 0; i
- scanf("%d", &q[i]);
- }
-
- while(m--){
- int x =0;
- scanf("%d", &x); // x即为这个要求边界的数
-
- int mid;
- //先看左边界
- int l = 0, r= n-1;
- while(l < r){
- mid = l + r >> 1;
- if(q[mid]>=x){
- //如果q[mid] ≥ x,说明,左边界在mid左边或者就是mid
- r = mid;
- }else{
- l = mid+1; //这个地方是+1,所以更新mid的时候不用加1,不会造成死循环
- }
- }
- //最后的时候,l 会跟 r相遇,来判断一下这个时候的情况,如果是x,那就是找到了,如果不是x 那就是数组里都没有x
- if(q[l] == x){
- cout << l << " ";
- }else{
- cout << "-1 -1" << endl;
- continue;// 在这里就可以跳出这一层了
- }
-
- //在判断右边界
- l=0, r=n-1;
- while(l
- mid = l + r +1 >> 1;
- if(q[mid] > x){
- //如果q[mid]大,说明右边界,在mid左边
- r = mid-1; //这个地方减一了 就要记得mid更新的地方+1一下;
- }else{
- l = mid;
- }
- }
- cout << l << endl; //有左边界就肯定有右边界,所以这里不用更新
- }
- return 0;
- }
2.2 浮点数二分
2.2.1 基本思想
也是找到一个边界,但是 每次都是严格的分成一半,就不需要考虑边界加一,只要保证答案一直在区间之内就行,然后在区间足够小的时候,就认为是一个数了,例如说已经达到10的-8次方了,就认为 l = r
2.2.2 看个题目
求一个数的三次方根,题目要求精度在-6次方,来看代码:
- #include
- using namespace std;
-
- //浮点数的二分问题,看起来比整数二分更加简单
-
- int main(){
- double x; // 注意这里的类型是double,
- cin >> x;
- double l = -100, r = 100; //注意题目给的数的三次方后,有正有负,且答案都在-100 到100 之间,注意区间别再携程0——x了
- while(r-l > 1e-8 ){
- double mid = (l+r)/2;
- if(mid*mid*mid >= x){
- r = mid;
- }else{
- l = mid;
- }
- }
-
- printf("%lf\n", l); //默认保留6位小数,C语言四舍五入
- //%f代表单精度浮点型数据(float),%lf代表双精度浮点型数据(double)。
-
- return 0;
- }
来自y总的经验:如果题目要求的精度是-4次,那就算到-6,如果要求-5 就算到-7,总之每次都要多两位小数,这样可以避免误差
-
相关阅读:
.....
Linux安装Nexus3搭建maven私服超详细搭建上传步骤
OV5640 低功耗使用说明- PowerDown模式
DNS协议、ICMP协议、NAT技术
真能处,180公里的纯电续航,这款车居然一点都没亏
Linux 远程工具 基础命令
使用seata实现分布式事务
长春大学计算机考研资料汇总
jquery链接导入和基本选择器的使用
Springboot整合SpringCache+redis简化缓存开发
-
原文地址:https://blog.csdn.net/qq_46485137/article/details/127734480