【AcWing_算法基础课】二分查找 : 整数二分、浮点数二分

主要思想:

将区间分为两个子区间,然后下一次就处理目标所在的区间。每一次都保证目标在区间里,区间长度为1时,查找成功。

本质并非单调性,本质是找到某种性质,使得区域可以划分为两个小区域, 其中左边区域均满足该性质,右边区域均不满足该性质 。二分就这可以寻找这个区域的边界。

例如区间前半部分满足性质a,后半部分满足性质b,那么就可以用二分来 找这两个区间的分界点

  • 它可以是第一个区间的最后一个(下面的①,最后一个满足该性质的点)
  • 也可以是第二个区间的第一个(下面的②,第一个不满足该性质的点)

在这里插入图片描述
例如模板1中,若是mid满足check()的性质,将区域从[l,r]更新为[mid,r],操作为l = mid;

yxc二分查找模板:

//寻找红色区域边界1
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;	//+1是为了避免死循环
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

//寻找绿色边界2
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    return l;
}

记忆: 第①种情况要加1




整数二分模板题:

原题链接:AcWing.789 数的范围

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

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

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

输入格式

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

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

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

输出格式

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

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

数据范围

  • 1≤n≤100000
  • 1≤q≤10000
  • 1≤k≤10000

输入样例:

6 3
1 2 2 3 3 4
3
4
5

输出样例:

3 4
5 5
-1 -1

思路:

这是一道好题,一道题中讲二分的两个模板都用上了
找区间也就是找左右端点:

  • 左端点是第一个大于等于k的数
  • 右端点是最有一个大于等于k的数

C++代码:

#include <iostream>
using namespace std;

const int maxn = 1e5 + 10;
int n, a[maxn], q;

int main(){
    scanf("%d %d", &n, &q); //n数组长度 q询问长度
    for(int i = 0; i < n; i++ ) scanf("%d", &a[i]);
    
    while(q--){
        int k;  //k要查找的值
        scanf("%d", &k);
        
        int l = 0, r = n - 1;
        while(l < r){   //找起始位置,找第一个大于等于k的数
            int mid = l + r  >> 1;
            if(a[mid] >= k) r = mid;
            else l = mid + 1;
        }
        //当找到满足大于等于k的第一个数不等于k时,说明数组中不存在k
        if(a[l] != k) cout << "-1 -1" << endl;
        else{
            cout << l << " " ;
            int l = 0, r = n - 1;
            while(l < r){   //找终止位置,找最后一个小于等于k的数
                int mid = l + r +1>> 1;
                if(a[mid] <= k) l = mid;
                else r = mid - 1;
            }
            cout << l << endl;
        }
    }
    
    return 0;
}

复杂度分析:

  • 时间复杂度:O(qlogn),有q个询问,处理每个k,需要进行两次二分
  • 空间复杂度:O(1),常数个临时变量

浮点数二分模板题:

原题链接:AcWing .790 数的三次方根
给定一个浮点数 n,求它的三次方根。

输入格式

共一行,包含一个浮点数 n。

输出格式

共一行,包含一个浮点数,表示问题的解。

注意,结果保留 6 位小数。

数据范围

−10000≤n≤10000

输入样例:

1000.00

输出样例:

10.000000

思路:

利用中点的三次方和x进行比较
选择递归到左半区间或者递归到右半区间
结束递归有两种方式:

  • 当l和r足够接近的时候停止,一般是1e-8
  • 设置循环的次数,比如循环100次

C++代码:

#include <iostream>
using namespace std;

int main(){
    double x;
    scanf("%lf", &x);
    
    double l = -10000, r = 10000;
    while(r - l > 1e-8){
        double mid = (l + r)/2;
        if(mid * mid * mid <= x) l = mid;
        else r = mid;
    }
    printf("%lf", l);

    return 0;
}

复杂度分析:

  • 时间复杂度:O(logn)
  • 空间复杂度:O(1)

注:

  1. 浮点数二分可以准确求出mid,不用考虑边界,比整数二分简单
  2. 开三次方函数:cbrt(),需要导入cmath
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值