二分总结

二分总结

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

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

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

一、函数定义与用途

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

posted @ 2022-07-13 09:03  糖豆爸爸  阅读(75)  评论(0)    收藏  举报
Live2D