二分查找:第一个目标值,最后一个目标值,最近接的元素
二分查找
二分查找的第一个算法模板就是在有序数列中找到目标值第一次出现的位置,也就是我们常常讲的左端点。因为一个有序数列如果多个跟目标值相同的值,就会有左端点和右端点。
如果序列中没有目标值,这个时候题目大多会要求找出最近接目标值的那个元素。这个时候就要用到最接近元素的模板。
上代码
.
// An highlighted block
#include<bits/stdc++.h>
using namespace std;
int a[1000005],n,x,m;
int b1(int l,int r,int x){//lower_bound源码,找有序队列中的第一个x(左端点)
while(l<r){//当两个端点的指针重合的时候,说明找到目标值
int mid=(l+r)/2;//求二分中点
if(a[mid]>=x) r=mid;//中间值比目标值大或者等于,目标值在左边的区域,这里因为有等于号,所以不能舍去mid
else l=mid+1;//否则在右边的区域,这里因为没有等于号,所以可以舍去mid
}
return l;
}
int b2(int l,int r,int x){//upper_bound,找有序队列中的最后一个x(右端点)
while(l<r){//当两个端点的指针重合的时候,说明找到目标值
int mid=(l+r+1)/2;//求二分中点,这里要+1,因为可以使mid的位置居中向右
if(a[mid]<=x) l=mid;//中间值比目标值小或者等于,目标值在右边的区域,这里因为有等于号,所以不能舍去mid
else r=mid-1;//否则在左边的区域,这里因为没有等于号,所以可以舍去mid
}
return l;
}
int b3(int l,int r,int x){//查找最接近的元素
if(x<=a[l]) return a[l]; //先判断目标值是不是超出查找范围
if(x>=a[r]) return a[r];
while(l+1<r){//这里要主要,当l和r的差值为1的时候就要停止查找,然后判断那个值更适合
int mid=(l+r)/2;
if(a[mid]>=x) r=mid;
else l=mid;//这里要注意的是,都不需要进行+-1,因为有可能不存在等于的情况,
//所以不能忽略任意一个数
}
int p=abs(a[l]-x);
int q=abs(a[r]-x);
return (p<=q? a[l]:a[r]);//返回更接近的那个元素
}
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
sort(a,a+n);
cin>>m;
while(m--){
cin>>x;
cout<<b3(0,n-1,x)<<endl;
}
return 0;
}