基础算法入门03——二分算法

本文介绍了基础算法入门中的二分查找技术,包括整数和浮点数的二分策略,并展示了如何在有序数组中寻找特定元素的起始和终止位置。通过实例演示了整数二分查找的模板及其避免死循环的方法,以及浮点数二分的特性和应用场景。
摘要由CSDN通过智能技术生成

基础算法入门03——二分算法

整数二分

在这里插入图片描述

对于能采取二分的方法来解决的问题主要需要满足如下条件:

我们能够找到一个性质,使得可以将所有元素分成两部分LeftRight,然后我们可以通过二分来获得满足这个性质的边界,或者是不满足这个性质的边界

比如说,对于一般数组的二分查找

1 2 2 4 5 6 6 6 7

如果我们要查找到数字5,那么对于这个有序数组,可以找到一个性质大于等于5,显然,这个性质是能够将数组分成两份,而我们要查找的5正好就是满足这个性质的一个边界

因为对于数组的下标都是整数,所以边界是有间隙的,也就是有两个index_a,index_b

而对于查找不同的边界,就会有两套不同的模板,主要是用来避免出现死循环的情况发生

模板1——查找index_a

int binary_search(int l,int r)
{
    
    while(l<r)// 当l=r的时候就是搜索结束了
    {
        int mid=l+r+1>>1;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    return l;
}

通过mid将元素先对半分,check()函数是用来判断mid个元素是否满足这个性质,从而来判断下一次我们搜索范围该怎么更新(关于l和r的更新),true时,l=mid,否则r=mid-1

(对于check()的模拟)

image-20220626015154906

模板2——查找index_b

int binary_search(int l,int r)
{
    
    while(l<r)// 当l=r的时候就是搜索结束了
    {
        int mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    return l;
}

对于要查找index_b,和上面的几乎差不多,主要是要看你怎么更新r和l

两个模板的不同之处在于

  1. //模板1
    mid=l+r+1>>1
    

    这里mid的设置多了个+1,原因是在check()==true的时候

    if(check(q[mid])) r=mid;
    

    因为电脑一般的整数除法是下取整,当r=l+1的时候 mid=(l+r)/2=l,这个时候l更新就会一直更新成l,那么就会陷入死循环,所以我们+1就是帮助他上取整,当r=l+1的时候,l进行更新之后等于r了,就会跳出循环。

(对于check()的模拟)

image-20220626015259590

但是,这两个算法只需要记住一个就够了

使用算法三步走

  • 第一步:找到性质,设置check()函数
  • 第二步:根据check()函数的结果来更新r and l
  • 第二步:如果r=mid l=mid+1,mid设置成l+r>>1;如果l=mid r=mid-1,mid设置成l+r+1>>1
题目描述

数的范围

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

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

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

输入格式

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

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

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

输出格式

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

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

数据范围

1 ≤ n ≤ 100000 1 \le n \le 100000 1n100000
1 ≤ q ≤ 10000 1 \le q \le 10000 1q10000
1 ≤ k ≤ 10000 1 \le k \le 10000 1k10000

输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1
算法分析

对于数列

1 2 2 3 3 4

我们要找元素的起始位置和中止位置

对于找到起始位置,我们可以发现一个性质1:起始位置右边的数都满足≥x

对于找到中止位置,我们可以发现一个性质2:中止位置左边的数都满足≤x

对于这两条性质,我们可以分别将数组分成两部分,而我们要找的正是性质的边界,因此我们使用二分算法试试

代码:
#include<iostream>
using namespace std;
const int N=1000010;
int q[N];
int main()
{
    int n,m;
    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,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,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;
}

浮点数二分

和整数二分的区别在于不用考虑陷入死循环的问题,也就是不需要区分l+r+1>>1

比如说求一个数的二次方根,我们要找的二次方根一定满足:0≤t≤x

边界就是[0,x],只有当l和r无限接近的时候,我们才能确定二次方根的值,而不是l==r

#include<iostream>
using namespace std;
int main()
{
    double x;
    cin>>x;
    double l=0,r=x;
    while(r-l>1e-6)
    {
        double mid=(l+r)/2;
        if(mid*mid<=x) l=mid;
        else r=mid;
    }
    printf("%.6lf",l);
    return 0;
}
# 输入
2
# 输出
1.414213
# 输入
4
# 输出
2.000000
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值