用stl函数来实现二分查找

算法日记(一)_m0_61723200的博客-CSDN博客

之前在这篇文章用的是自定义函数的方法进行二分查找的,今天我会用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;
}

  • 11
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

forget hurt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值