秒懂二分算法

AcWing算法——二分算法



前言

  我们今天进入到二分算法,之前总结了快速排序和归并排序的模板,没有细分其中数据到底是什么类型,今天的二分算法我们根据数据的类型分为两个模板(整数二分,浮点数二分),每个模板都有自己适应的情况,大家可以根据题目的不同选择不同的模板进行套用。


一、二分基本概念

  二分的重点是求边界而不是单调性,在讲二分之前必须明白这一点,很多人认为最重要的是单调性但是y总也详细分析了,二分的本质是边界。有单调一定可以二分,但是没有单调也可能使用二分。我们将二分分为了整数二分和浮点数二分两种,并且对这两种情况都会分享对应的模板。

二、整数二分

2.1 整数二分思路

我们先在左边的区域找右边界
1.先写一个check函数,这个函数的目的是用来判断mid是否符合该区间的性质,然后根据判断的结果来确定边界的位置在哪里
2.判断左边区域右边界,找到mid,如果mid符合这个区域的性质那么就说明mid在这个右边界的左边也就是说mid在这个区域内,我们要找边界就要在mid的右边找,所以范围就变成了[mid,r]。
如果check函数判断是false,说明mid不符合这个区域的性质,那么右边界就在mid的左边,mid不符合所以我们区间调整为[l,mid-1].
然后我们在右边的区域找左边界,找的方法是一样的,但是一定注意边界的改变。

2.2 整数二分图示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.3 整数二分模板

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;
}

2.4 练习题目—数的范围

在这里插入图片描述
在这里插入图片描述

2.5 题目解法代码

#include<iostream>
using namespace std;
const int N=100010;
int n,m;
int q[N];

int main()
{
    
    
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++) scanf("%d",&q[i]);
    while(m--)
    {
    
    
        int x;
        scanf("%d",&x);
        
        int l=0;int r=n-1;
        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;int 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;
}

三、浮点数二分

3.1 浮点数二分图示

在这里插入图片描述

3.2 浮点数二分模板

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;
}

总结

  今天分享了二分的两个两个模板,分别应对整数的情况和浮点数的情况,相对而言整数的情况更难处理,因为有边界问题需要注意,但是浮点数就更简单了,不存在边界问题,只要保证我们的答案在所取区间内部,然后一直二分直到满足精度就表示找到了,希望我的博客对大家有所帮助,欢迎大家在评论区畅所欲言~

猜你喜欢

转载自blog.csdn.net/MDLYB/article/details/127718577