二分查找(入门)

二分查找(蒟蒻篇)

二分查找定义:

二分查找法,又称折半查找法。它是一个很高的查找方法。时间复杂度是log(N)。
我感觉就是枚举答案的方法,只是比暴力枚举快了很多

但该方法是建立在有序的前提下的,基本思路就是:先找到答案范围 [ l , r ] 的中间值mid,然后用根据题意写的check函数判断更新 l or r 。

反例

有一天小明到图书馆借了 N 本书,出图书馆的时候,警报响了,于是保安把小明拦下,要检查一下哪本书没有登记出借。小明正准备把每一本书在报警器下过一下,以找出引发警报的书,但是保安露出不屑的眼神:你连二分查找都不会吗?于是保安把书分成两堆,让第一堆过一下报警器,报警器响;于是再把这堆书分成两堆…… 最终,检测了 log(N) 次之后,保安成功的找到了那本引起警报的书,露出了得意和嘲讽的笑容。于是小明背着剩下的书走了。 从此,图书馆丢了 N - 1 本书。


所以这个故事告诉我们,二分查找的条件

  • 用于查找的内容逻辑上来说是需要有序的
  • 在[l,r]的范围内,答案只有一个

模板

Y总的三个模板

两个整数模板

第一个

作用是:找符合条件的最小值

while (l<r)
{
	auto mid=l+r>>1;
	if (check(mid))	r=mid;
	else l=mid+1;
}
第二个

作用是:找符合条件的最大值

while (l<r)
{
       auto mid=l+r+1>>1;
       if (check(mid)) l=mid;
       else r=mid-1;
}

这么说可能有点抽象,找个题来形象说明
这个题是acwing基础课的题 数的范围

这个题的意思:给你一个数列,查询一个数的起始和终止位置,如果没有输出-1 -1.
给你一个例子:1 2 3 3 3 4 4 5
让你查询 3 的起始和终止位置,起始位置就相当于满足>=3的最小位置,而终止位置就是<=3的最大位置。用两个模板跑一下就行

#include <iostream>
using namespace std;
int p[100010];
int main ()
{
    int n,m;
    cin>>n>>m;
    for (int i=0;i<n;i++)   scanf ("%d",&p[i]);
    for (int i=0;i<m;i++)
    {
        int k;
        scanf ("%d",&k);
        int l=0,r=n-1;
        while (l<r)
        {
            int mid=l+r>>1;
            if (p[mid]>=k)  r=mid;
            else l=mid+1;
        }
        if (p[l]!=k)
        {
            printf("-1 -1\n");
        }
        else
        {
            printf("%d ",l);
            int l=0,r=n-1;
            while (l<r)
            {
                int mid=l+r+1>>1;
                if (p[mid]<=k)  l=mid;
                else r=mid-1;
            }
            printf("%d\n",l);
        }
    }
}

浮点数模板

假设题目取到小数点后三位,那么r-l到1e-5就够了
while(r-l>1e-5)
{
	auto mid=(l+r)/2;	//浮点数就不能用向右移来除二
	if(check(l))	l=mid;//(r=mid) 这还是要看题的
	else r=mid;//(l=mid)
}

相信仔细看的都发现了,整数两个模板的区别,一个为啥第二个mid=l+r+1>>1,为啥要加一呢,因为当l=r-1的时候,如果check返回的是true,那么如果mid=l+r>>1,c++整数除法是向下取整,所以l还是=r-1,然后就T了,所以mid=l+r+1>>1

例题

查找

给你一个有序的数列,n个数,m个查询,查询一个数是否出现在数列中,如果存在就输出
第一次出现的编号,否则输出-1

板中板的题

#include <iostream>
using namespace std;
int p[1000010];
int main ()
{
    int n,m;
    cin>>n>>m;
    for(int i=0;i<n;i++)    scanf ("%d",&p[i]);
    while (m--)
    {
        int l=0,r=n-1;
        int k;
        scanf ("%d",&k);
        while (l<r)
        {
            int mid=l+r>>1;
            if (p[mid]>=k)  r=mid;
            else l=mid+1;
        }
        if (p[r]==k)    printf("%d ",r+1);
        else printf("-1 ");
    }
}

STL

c++自带一个简单的二分查找函数 lower_bound()和upper_bound

lower_bound(p,p+n,k) 返的是在p数组从下标0开始后n个数,第一个≥k的第一个数的地址
upper_bound(p,p+n,k) 第一个>k的第一个数的地址
如果你想要下标,以lower_bound为例,就是lower_bound(p,p+n,k)-p,这就是第一个≥k的第一个数的下标

题单

这都是一些我做完的题,不理解的地方可以和我讨论
洛谷题
二分查找题单
atcoder上的一个题
E - Dist Max 2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值