• 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

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

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

    1. /*
    2. 这个题是需要求输入的数,在数组中的起始位置和结束位置
    3. 像这种求边界的问题就 很适合用 二分查找 来解决问题
    4. */
    5. #include
    6. using namespace std; //前两行 不练就会忘记了,嘤嘤嘤
    7. const int N = 100010;
    8. int q[N];
    9. int main(){
    10. int n, m;
    11. scanf("%d%d", &n, &m);
    12. for(int i = 0; i
    13. scanf("%d", &q[i]);
    14. }
    15. while(m--){
    16. int x =0;
    17. scanf("%d", &x); // x即为这个要求边界的数
    18. int mid;
    19. //先看左边界
    20. int l = 0, r= n-1;
    21. while(l < r){
    22. mid = l + r >> 1;
    23. if(q[mid]>=x){
    24. //如果q[mid] ≥ x,说明,左边界在mid左边或者就是mid
    25. r = mid;
    26. }else{
    27. l = mid+1; //这个地方是+1,所以更新mid的时候不用加1,不会造成死循环
    28. }
    29. }
    30. //最后的时候,l 会跟 r相遇,来判断一下这个时候的情况,如果是x,那就是找到了,如果不是x 那就是数组里都没有x
    31. if(q[l] == x){
    32. cout << l << " ";
    33. }else{
    34. cout << "-1 -1" << endl;
    35. continue;// 在这里就可以跳出这一层了
    36. }
    37. //在判断右边界
    38. l=0, r=n-1;
    39. while(l
    40. mid = l + r +1 >> 1;
    41. if(q[mid] > x){
    42. //如果q[mid]大,说明右边界,在mid左边
    43. r = mid-1; //这个地方减一了 就要记得mid更新的地方+1一下;
    44. }else{
    45. l = mid;
    46. }
    47. }
    48. cout << l << endl; //有左边界就肯定有右边界,所以这里不用更新
    49. }
    50. return 0;
    51. }

    2.2 浮点数二分

    2.2.1 基本思想

            也是找到一个边界,但是 每次都是严格的分成一半,就不需要考虑边界加一,只要保证答案一直在区间之内就行,然后在区间足够小的时候,就认为是一个数了,例如说已经达到10的-8次方了,就认为 l = r

           

    2.2.2 看个题目

    AcWing 790. 数的三次方根 - AcWing

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

    1. #include
    2. using namespace std;
    3. //浮点数的二分问题,看起来比整数二分更加简单
    4. int main(){
    5. double x; // 注意这里的类型是double,
    6. cin >> x;
    7. double l = -100, r = 100; //注意题目给的数的三次方后,有正有负,且答案都在-100 到100 之间,注意区间别再携程0——x了
    8. while(r-l > 1e-8 ){
    9. double mid = (l+r)/2;
    10. if(mid*mid*mid >= x){
    11. r = mid;
    12. }else{
    13. l = mid;
    14. }
    15. }
    16. printf("%lf\n", l); //默认保留6位小数,C语言四舍五入
    17. //%f代表单精度浮点型数据(float),%lf代表双精度浮点型数据(double)。
    18. return 0;
    19. }

    来自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