深入理解二分查找

155 阅读1分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第4篇笔记

理解下面这4种情况的二分查找,那么你就再也不会在二分查找这里出错了!

二分的核心就是四种情况

其中选择(l + r)还是(l+r+1)取决于你想逼近左边还是逼近右边 mid = (l + r) / 2 还是 mid = (l + r + 1) / 2 取决于你想逼近左边还是右边,这两类是不同的二分!

二分模板-来自于yxc

bool check(int x) {/* ... */} // 检查x是否满足某种性质

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

bool check(double x) {/* ... */} // 检查x是否满足某种性质

double bsearch_3(double l, double r)
{
    const double eps = 1e-6;   // eps 表示精度,取决于题目对精度的要求
    while (r - l > eps)
    {
        double mid = (l + r) / 2;
        if (check(mid)) r = mid;
        else l = mid;
    }
    return l;
}

实战的四种情况(以vector举例)

vector<int> a = {1, 2, 2, 3, 3, 3, 4, 4, 5, 6};

// 4类二分!万物均源自于该4类,其中1和3算同类,2和4算同类
// mid = (l + r) / 2 还是 mid = (l + r + 1) / 2 取决于你想逼近左边还是右边,这两类是不同的二分

// 1.找到第一个3的前一个位置
int l = 0, r = a.size() - 1;
while(l < r){
    int mid = (l + r + 1) / 2;
    if(a[mid] < 3){
        l = mid;
    }else{
        r = mid - 1;
    }
}
cout<<l<<endl;


// 2.找到第一个3的位置
int l = 0, r = a.size() - 1;
while(l < r){
    int mid = (l + r) / 2;
    if(a[mid] >= 3){
        r = mid;
    }else{
        l = mid + 1;
    }
}
cout<<l<<endl;

// 3.找到最后一个3的位置
int l = 0, r = a.size() - 1;
while(l < r){
    int mid = (l + r + 1) / 2;
    if(a[mid] <= 3){
        l = mid;
    }else{
        r = mid - 1;
    }
}
cout<<l<<endl;

// 4.找到最后一个3的后一个位置
int l = 0, r = a.size() - 1;
while(l < r){
    int mid = (l + r) / 2;
    if(a[mid] > 3){
        r = mid;
    }else{
        l = mid + 1;
    }
}
cout<<l<<endl;