Acwing算法基础学习笔记(二)二分

目录

二、二分

2.1 整数二分

2.1.1 基本思想

2.1.2 主要步骤

2.1.3 看个题目

2.2 浮点数二分

2.2.1 基本思想

2.2.2 看个题目


二、二分

2.1 整数二分

2.1.1 基本思想

        二分的本质:二分就是找到一个性质,可以将一组数据一分为二,使得一边是满足的,另一边是不满足的,二分就是用来寻找这个性质的边界,并且保证性质在区间之内

        整数二分就是对整数进行二分,需要小心对应找中间值的处理,小心陷入死循环

        二分可以解决单调性的问题,但是没有单调性也不是不可以二分

2.1.2 主要步骤

        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  是不是我们要找的,就完成了。

2.1.3 看个题目

Acwing 789题  数的范围   链接:  AcWing 789. 数的范围 - AcWing

像这种查找问题,特别是查找边界的问题,感觉 都会用二分,

这个题的题目描述和解题代码如下:

/* 
    这个题是需要求输入的数,在数组中的起始位置和结束位置
    像这种求边界的问题就 很适合用 二分查找 来解决问题
*/

#include <iostream>
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<n;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<r){
            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 看个题目

AcWing 790. 数的三次方根 - AcWing

求一个数的三次方根,题目要求精度在-6次方,来看代码:

#include <iostream>
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,总之每次都要多两位小数,这样可以避免误差

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值