算法笔记——二分

209 阅读2分钟

「这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战

什么是二分

二分的含义:把一个有序的一维数组分成两个区间,取一个区间,然后在对其进行对半划分,不断往复直到最后搜寻出正确的结果

image-20211128201450165

二分的作用

二分一般用来进行查找,相比于一般的遍历搜寻可以达到更快的时间复杂度。当题目给定了一个有序数组

(升序或者降序),你可以放心大胆的使用二分进行搜寻。

二分的公式模板

这里有两种情况:

image-20211128204108453

如果你想要的答案在1区间,可以使用这套模板:

// 区间[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;
}

如果你想要的答案在2区间,可以使用这套模板:

// 区间[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;
}

具体的题目

给定一个按照升序排列的长度为 nn 的整数数组,以及 qq 个查询。

对于每个查询,返回一个元素 kk 的起始位置和终止位置(位置从 00 开始计数)。

如果数组中不存在该元素,则返回 -1 -1

输入格式

第一行包含整数 nn 和 qq,表示数组长度和询问个数。

第二行包含 nn 个整数(均在 1∼100001∼10000 范围内),表示完整数组。

接下来 qq 行,每行包含一个整数 kk,表示一个询问元素。

输出格式

共 qq 行,每行包含两个整数,表示所求元素的起始位置和终止位置。

如果数组中不存在该元素,则返回 -1 -1

数据范围

1≤n≤1000001≤n≤100000 1≤q≤100001≤q≤10000 1≤k≤100001≤k≤10000

输入样例:

6 3
1 2 2 3 3 4
3
4
5

输出样例:

3 4
5 5
-1 -1

代码展示

#include<iostream>
using namespace std;
const int N=1e6+10;
int q[N];
int m,n;
int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++) scanf("%d",&q[i]);
    while(m--){
        int x;
        int l=0,r=n-1;
        cin>>x;
        while(l<r){
            
            //开始二分
            int mid=l+r>>1;
            if(q[mid]>=x) r=mid;
            else l=mid+1;
            
        }
        if(q[l]!=x){
            
            cout<<"-1 -1"<<endl;
        }else{
            cout<<l<<" ";
            int l=0,r=n-1;
            
            while(l<r){
                int mid=l+r+1>>1;
                if(q[mid]<=x) l=mid;
                else r=mid-1;
            }
            cout<<l<<endl;            
        }                      
    }                
    return 0;
}