二分查找和二分答案

二分查找

二分的思想在程序设计中有着广泛的应用,例如,排序算法中的快速排序、归并排序,数据结构中的二叉树、堆、线段树等。二分是一种常用且高效的算法,它的基本用途是在单调序列中进行查找和判定操作。

对于n个有序且没有重复的元素(假设为升序),从中查找特定的某个元素x,我们可以将有序序列分成规模大致相等的两部分,然后取中间元素与要查找的元素x进行比较,如果x等于中间元素,则查找成功,算法终止;如果x小于中间元素,则在序列的前半部分继续查找,否则在序列的后半部分继续查找。这样就可以将查找的范围缩小一半,然后在剩余的一半中继续重复上面的方法进行查找。

这种每次都从中间元素开始比较,并且一次比较后就能把查找范围缩小一半的方法,叫作二分查找。二分查找的时间复杂度是 O(logN),是一种效率较高的查找算法。

时间复杂度O(logN)

在常见的时间复杂度中,时间复杂度为O(logN)的算法十分高效,当数据规模增大n信时,耗时增大logN倍(这里的log以2为底)。例如,当数据增大256倍时,耗时增大8倍,因为2的8次方等于256。二分查找算法的时间复杂度为O(logN),每次查找排除掉一半的可能,1024个有序数据中最多查找10次就可以找到目标。

二分查找算法描述

用一维数组a存储有序元素序列,用变量low和high分别表示查找范围中第一个元素和最后一个元素的下标,mid表示查找范围的中间位置对应元素的下标,x为要查找的元素。

(1)变量初始化,令low=1,high=n。low和high分别初始化为有序序列的第一个元素和最后一个元素的下标。

(2)判断查找范围low≤high是否成立,如果成立,执行(3),否则输出"-1"(表示没有找到),结束算法。

(3)取中间元素,令mid=(low+high)/2,a[mid]就是中间元素。

(4)比较a[mid]与x,如果a[mid]等于x,则查找成功,结束算法;如果x<a[mid],则在序列的前半部分进行查找,修改查找的上界high=mid-1,下界不变,否则将在序列的后半部分进行在找,修改查找的下界low=mid+1,上界不变,转到(2)。

例如,从10个元素13,15,18,22,28,34,56,65,70.80中查找元素22的过程如图111-1所示最坏的情况下 4次可以找到。

特别注意:使用二分查找时,必须保证数据是有序的,若数据是无序的,则需要使用排序算法将数据变得有序。

二分查找算法的框架如下:

int ef(int a[],int n,int x)
{
    int low=1,high=n,mid;
    while(low<=high)   //判断查找范围low<=high是否成立 
    {
        mid=(low+high)/2;   //取中间元素的位置 
        if(x==a[mid])    //x已经找到 
        {
            return mid;    //返回x对应的下标 
        }
        else if(x<a[mid])
        {
            high=mid-1;     //调整high,在前半部分查找 
        }
        else low=mid+1;   //调整low,在后半部分查找 
    }
    return -1;
} 

Copy

思考:为什么while循环的条件中是“<=”,而不是“<”?

例题1:查找m个数(主题库2646)

请你输入一个含 nn 个数字的不重复数列,请你高速的在这个数列中寻找 mm 个数字 x_1,x_2,...,x_mx1​,x2​,...,xm​ ,如果能找到直接输出,如果不存在输出 -1−1 ,用换行隔开(0<m<n<=10^60<m<n<=106)

输入格式:

输入共 4 行,第一行,为一个数字 nn 。第二行为 nn 个数字。第三行为一个数字 mm 。第四行为 mm 个要寻找的数字。

输出格式:

输出共 mm 行,每行一个数字,如果能找到直接输出原数字,如果找不到,输出 -1−1 。

样例输入

5
1 2 3 4 5 
3
2 1 3

Copy

样例输出

2
1
3

Copy

问题分析

解法一:暴力,样例没问题,提交0分。

#include<bits/stdc++.h> 
using namespace std;
int a[1000001];
int main()
{ 
    int n,m,i,j,t;
    cin>>n; 
    for(i=1;i<=n;i++)
    { 
        cin>>a[i]; 
    }
    cin>>m;
    for(i=1;i<=m;i++)
    { 
        cin>>t;
        for(j=1;j<=n;j++)
        { 
            if(a[j]==t)
            { 
                cout<<j<<endl;
                break;
            }
        }
    }
    return 0;
}

Copy

解法2:二分查找+递归

#include<bits/stdc++.h> 
using namespace std;
int a[1000001];
int lower_bound(int x,int y,int t)
{ 
    if(x>y){ return -1; }
    int h=(x+y)/2;
    if(a[h]==t) { return a[h]; }
    if(a[h]>t)  { y=h-1; return lower_bound(x,y,t); }
    else { x=h+1; return lower_bound(x,y,t); }
}
int main()
{ 
    int n,m,i,j,t;
    cin>>n; 
    for(i=1;i<=n;i++) //一百万次
    { cin>>a[i]; }
    sort(a+1,a+1+n); //n*log(n)2千万次
    cin>>m;
    for(i=1;i<=m;i++)
    { 
        cin>>t; 
        cout<<lower_bound(1,n,t)<<endl;
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

10247D

我会继续努力,信息技术会越好

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

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

打赏作者

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

抵扣说明:

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

余额充值