• 二分总结


    二分总结

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

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

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

    一、函数定义与用途

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

  • 相关阅读:
    jQuery万能放大镜插件(普通矩形放大镜)
    Mysql增量写入Hdfs(二) --Storm+hdfs的流式处理
    Mysql增量写入Hdfs(一) --将Mysql数据写入Kafka Topic
    Spark SQL,如何将 DataFrame 转为 json 格式
    贝叶斯分类算法实例 --根据姓名推测男女
    从分治算法到 Hadoop MapReduce
    Actor模型浅析 一致性和隔离性
    写博客的思考
    scala模式匹配详细解析
    [转] Scala 中的异步事件处理
  • 原文地址:https://www.cnblogs.com/littlehb/p/16472541.html
Copyright © 2020-2023  润新知