二分查找
基本概念:
二分法,也称为折半搜索或对数搜索,是一种在有序数组中查找特定元素的算法。其基本思想是通过不断将查找范围缩小一半,从而快速定位目标元素。时间复杂度为O(logN)
模板:
1.从n个数中查找最后一个<=q的数的下标
最小化查找
从前往后的最后一个或从后往前的第一个,通俗点讲,就是尽量往右找到你想要的数
int find(int q){
int l=0,r=n+1;
while(l+1<r){//即l+1=r的时候停止查找
int mid=l+r>>1;
if(a[mid]<=q) l=mid;
else r=mid
}
return l;
}
其中l+r>>1
含义为l+r右移一位,即(l+r)/2下取整。
2.从n个数中查找第一个>=q的数的下标
最大化查找
从前往后的第一个或从后往前的倒数第一个,相对的,就是尽量往左找到你想要的数
int find(int q){
int l=0,r=n+1;
while(l+1<r){
int mid=l+r>>1;
if(a[mid]>=q) r=mid;
else l=mid;
}
return r;
}
3.浮点二分:
求一个浮点数(-10000<=y<=10000)的三次方根
double find(double y){
double l=-100,r=100;
while(r-1>1e-5){//保证精度
double mid=(l+r)/2;
if(mid*mid*mid<=y) l=mid;//如果符合题目要求
else r=mid;
}
return l;
}
int mian(){
double y;scanf("%lf",&y);
printf("%.3lf\n",find(y));
return 0;
}
例题:
查找(Luogu P2249)
题目分析:
因为我们需要在单调不减的非负整数中找多个数字,并且要求使用较快的方式,所以可以想到用二分查找的方法。同时还会想到取消同步流,这也会增高我们代码的效率。
思路:
1.读取输入数组。
2.用循环处理每一次的询问。
3.因为是输出第一次出现的编号,那么可以用最小化查找。
4.考虑找不到的情况。
解答:
#include<iostream>
using namespace std;
const int N=1e6+10;//把数组开大一点
int arr[N];
int find(int q,int a[],int n)//二分模板
{
int l=0,r=n+1;
while(l+1<r)
{
int mid=l+r>>1;
if(a[mid]>=q)
{
r=mid;
}
else
{
l=mid;
}
}
if(a[r]==q)//如果找到了
{
return r;//返回正确答案
}
else
{
return -1;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>arr[i];
}
while(m--)
{
int q;
cin>>q;
int t=find(q,arr,n);
cout<<t<<" ";
}
return 0;
}
总结:
二分可以提高查找效率,在要处理的数据量过大的情况下可以考虑这个方法;
要注意用最大化查找还是最小化查找,根据题目意思选择合适的模板。