二分法(Binary Search)使用总结

主要使用场景场景

  • 在有序序列中确定边界的行为
  • 在处理许多问题的时候和前缀和一起出现

定义

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列

时间复杂度

在使用诸如数组之类的顺序储存结构时,二分查找法的时间复杂度是对数级别的,O(log2n)

基本思想

查找的集合是有序的,这样可以通过利用顺序的比较来减少比较次数.
查找的范围X是查找次数n的指数级别,一般的,X=2^n.

利弊分析

  • 比较次数少
  • 好写

  • 要求顺序储存结构
  • 要求有序的集合

语言相关

CPP

  • 通过对stl的sort可以很方便的进行二分查找
    涉及的函数主要有以下三个(需要头文件algorithm)

binary_search(起始地址,结束地址,要查找的数值)

返回的是是否存在这么一个数,是一个bool值。

lower_bound(起始地址,结束地址,要查找的数值)

lower_bound() 函数用于在指定区域内查找**不小于目标值(>=N)**的第一个元素。也就是说,使用该函数在指定范围内查找某个目标值时,最终查找到的不一定是和目标值相等的元素,还可能是比目标值大的元素。

重载格式
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,const T& val);

在 [first, last) 区域内查找不小于 val 的元素

ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,const T& val, Compare comp);

在**[first, last)** 区域内查找第一个不符合 comp 规则的元素
此外,该函数还会返回一个正向迭代器,当查找成功时,迭代器指向找到的元素;反之,如果查找失败,迭代器的指向和 last 迭代器相同。
参数说明:
其中,first 和 last 都为正向迭代器,[first, last) 用于指定函数的作用范围;
val 用于指定目标元素;
comp 用于自定义比较规则,此参数可以接收一个包含 2 个形参(第二个形参值始终为 val)且返回值为 bool 类型的函数,可以是普通函数,也可以是函数对象。

返回值

函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置。
可以使用这个函数的返回值-容器.begin()的方式来获取index
比如

vector<int>::iterator it=lower_bound(vec.begin(),vec.end(),20);
int index=it-vec.begin();

但需要注意
假设在一个0-19组成的大小是20的数组里查询19,那么这个index的值是19,查询20的时候,这个index的值是20(也就是实际上没有找到,这个值是数组的大小)
这里的实际范围[vec.begin(),vec.end())其中vec.end()就是20.可以通过下面这个代码检查是否找到:

if (it==vec.end())
    {
        printf("没找到\n");
    }

这里也可以看出,vector.begin()实际上指向的是数组的下标是0的值,而vector.end()实际指向下标是N的值(在size是N的数组里不存在,会segmentation fault.)

upper_bound(起始地址,结束地址,要查找的数值)

upper_bound() 函数定义在头文件中,用于在指定范围内查找**大于目标值(>N)**的第一个元素。

重载格式

和上文几乎一致lower_bound()

实现

CPP

int l,r,mid;
    l=0,r=X-1;
    while (l<r)
    {
        mid=(l+r)>>1;
        if (条件1)
        {
            r=mid;
        }else {
            l=mid+1;
        }
    }

注意

  • 如果查找结果在范围内,那么最终的r一定是满足条件1的最靠近左侧的下标
  • 如果查找的值在范围外,那么最后的r会停留在两端.(所以需要对是否在范围外做检测),常见的方法有1边界检测,2在左右两端加两个远大和远小的flag值.
  • 大部分情况下最终结果的l和r是相等的,但是mid可以不等(这话不是废话?俺是nt)
例子
#define X 10
#define searched 20
vector<int> list(X);
    for (int i = 0; i < X; i++)
    {
        list[i] = 2 * i;
    }
    //0 2 4 6 8 10 12 14 16 18
    //0 1 2 3 4 5  6  7  8  9
    int l,r,mid;
    l=0,r=X-1;
    while (l<r)
    {
        mid=(l+r)>>1;
        if (list[mid]>=searched)
        {
            r=mid;
        }else {
            l=mid+1;
        }
    }
    printf("%d %d %d\n",l,mid,r);
    printf("%d %d %d\n",list[l],list[mid],list[r]);

注意
在上述查找过程中,
查询-1得到
0 0 0
0 0 0
查询0得到
0 0 0
0 0 0
查询1得到
1 0 1
2 0 2
查询5得到
3 3 3
6 6 6
查询9得到
5 5 5
10 10 10
查询10得到
5 5 5
10 10 10
查询18得到
9 8 9
18 16 18
查询200得到
9 8 9
18 16 18

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值