之前在这篇文章用的是自定义函数的方法进行二分查找的,今天我会用stl函数进行二分查找.
binary_search:查找某个元素是否出现,返回bool型。
lower_bound:查找第一个大于或等于某个元素的位置。
upper_bound:查找第一个大于某个元素的位置。
先看道二分查找的题
二分搜索基础版
Description
给你一个长度为n的排列有序从小到大递增的数组, 还有q次查询, 询问一个数字在这个数组中出现的位置.
Input
第1行输入一个正整数n (1<=n<=10000001<=n<=1000000), 代表数组的长度.
第2行输入n个整数, 从小到大递增, 均在Int类型范围内, 且不重复.
第3行输入一个正整数q (1<=q<=100001<=q<=10000), 代表欲查询的次数.
第4行到第q+3行输入一个正整数, 代表欲查询的数字.
你可以认为所有输入数据均为合法输入.
Output
每行输出一个数字.
若存在这个数, 则输出对应数组位置的下标(从0开始).
若不存在这个数, 则输出-1.
Sample Input 1
5 1 3 5 7 9 3 1 7 2
Sample Output 1
0 3 -1
Hint
请不要尝试暴力查询.
首先,先看一下不用stl函数的解法
#include<iostream>
using namespace std;
int twofen(int a[],int x,int n);
int main()
{
int n,x,q;
scanf("%d",&n);//数组长度
int a[n];
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);//输入数组元素
}
scanf("%d",&q);//查询次数
for(int i=0;i<q;i++)
{
scanf("%d",&x);//每次要查的数
printf("%d\n",twofen(a,x,n));
}
}
int twofen(int a[],int x,int n)//该题的核心函数
{
int left=0,right=n-1;//左区间右区间
while(left<=right)//当左边大于右边时停止搜索
{
int middle=(left+right)/2;//中间值
if(x==a[middle])如果相等说明找到了
{
return middle;将中间值(它的下标)还给函数
}
if(x>a[middle])//如果大了
{
left=middle+1;//说明在右区间
}
else
{
right=middle-1;//小了,在左区间
}
}
return -1;//没找到返回-1
}
再看看stl库函数的二分法
#include<bits/stdc++.h>
using namespace std;
int main()
{
int M,a[200005],n,x;
scanf("%d",&M);//输入的数组元素个数
for(int i=0;i<M;i++)scanf("%d",&a[i]);//输入数组元素
scanf("%d",&n);//输入查找次数
for(int j=0;j<n;j++){
scanf("%d",&x);//输入每次查找的元素
int t=binary_search(a,a+M,x);///该二分查找函数为bool型,判断查找的数是否存在
int *k=lower_bound(a,a+M,x);///找到需要查找的数的第一个位置(为地址),即大于等于x的数
int *q=upper_bound(a,a+M,x);///找到比需要查找的数大的第一个数的第一个位置(为地址)
int p=q-a-(k-a);///q-a和k-a分别得出两者的数组下标
if(t){
if(p==1) ///数组中无重复数字x的情况
printf("%d",k-a);
else ///数组中有重复数字x的情况
for(int w=k-a;w<q-a;w++)printf("%d ",w);//k-a就是找的那个数的地址,q-a就是第一个比找的那个数大的
}
else printf("-1");
//puts("");
cout<<endl;
}
return 0;
}
再看一道进阶的吧
二分搜索进阶版
Description
给你一个长度为n的排列有序从小到大递增的数组, 还有q次查询, 现在希望将一个数字插入数组之中, 但是不破坏数组原来从小到大递增的性质, 想让你写个程序帮忙找插入的位置(只寻找, 并不会插入改变原数组).
Input
第1行输入一个正整数n (1<=n<=10000001<=n<=1000000), 代表数组的长度.
第2行输入n个整数, 从小到大递增, 均在Int类型范围内, 可能会重复.
第3行输入一个正整数q (1<=q<=100001<=q<=10000), 代表欲查询的次数.
第4行到第q+3行输入一个正整数, 代表欲插入的数字.
你可以认为所有输入数据均为合法输入.
Output
每行输出一个数字.
输出对应数组位置的下标(从0开始), 即插入该数字之后该数字的数组下标.
Sample Input 1
5 4 10 70 96 100 3 3 9 111
Sample Output 1
0 1 5
还是先看不用stl库函数的方法
#include<iostream>
using namespace std;
int n,q,x;
int main()
{
cin>>n;
int a[n];
for(int i=0;i<n;i++)
{
cin>>a[i];
}
cin>>q;
for(int i=1;i<=q;i++)//这属于模板二,可以看上面附的博客
{
cin>>x;//要找的数
int l=0,r=n;
while(l<r)//相等时停止搜索
{
int mid=l+r>>1;//位运算除2
if(a[mid]>=x)//如果大于等于要找的数
r=mid;//说明在左边
else
l=mid+1;//否则在右边
}
cout<<l<<endl;//因为二分法找的是最接近x的那个数字的位置
}
return 0;
}
stl函数做法
#include<bits/stdc++.h>
using namespace std;
int main()
{
int M,a[1000005]={0},n,x;
scanf("%d",&M);//输入的数组元素个数
for(int i=0;i<M;i++)scanf("%d",&a[i]);//输入数组元素
sort(a,a+M);//排序
scanf("%d",&n);//输入查找次数
for(int j=0;j<n;j++){
scanf("%d",&x);//输入每次查找的元素
//int t=binary_search(a,a+M,x);///该二分查找函数为bool型,判断查找的数是否存在
int *k=lower_bound(a,a+M,x);///找到需要查找的数的第一个位置(为地址),即大于等于x的数
//int *q=upper_bound(a,a+M,x);///找到比需要查找的数大的第一个数的第一个位置(为地址)
//int p=q-a-(k-a);///q-a和k-a分别得出两者的数组下标
if((k-a)>=0){
//if(p==1) ///数组中无重复数字x的情况
printf("%d",k-a);
//else ///数组中有重复数字x的情况
//for(int w=k-a;w<q-a;w++)printf("%d ",w);//k-a就是找的那个数的地址,q-a就是第一个比找的那个数大的
}
else//如果没找就说明给出的那个x要不大于最小值,要不大于最大值,但是最小值的情况可以排除了
{
cout<<M;
}
//puts("");
cout<<endl;
}
return 0;
}