因为每个人二分的风格不同,所以在学习二分的时候总是被他们的风格搞晕。有的人二分风格是左闭右开也就是[L,R),有的人是左开右闭的(L,R]。

二分的最基本条件是,二分的序列需要有单调性

下面介绍的时候用v来代表我们二分的目标,用第一个大于v,第一个大于等于v【升序】,最后一个小于v,最后一个小于等于v【降序】来描述,这里可以看到我即将要介绍的4种二分搜索。

1.第一个大于等于v

这就是我们常说的lower_bound()了,这是系统里面自带的库函数,下面是这个函数的原型:

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

其中first代表左边界迭代器,last代表右边界迭代器(注意左闭右开),val代表你要搜索的值,comp代表排序规则(这个参数在你对非结构体数组二分的时候并不需要,有默认规则)。

实例:

int a[100]={1,2,3,3,4,5,5,6,8,9,22},n;
while(cin>>n){
int p=lower_bound(a,a+11,n)-a;
//如果a是vector,那么lower(a.begin(),a.end(),v)-a.begin();
//你也可以在指定在[L,R)区间内二分lower_bound(a.begin()+L,a.begin()+R,v)-a.begin(),数组也是同理的
cout<<p<<endl;//这里输出的是第一个大于等于n的数的下标
}

接下来我们手写一个lower_bound(),风格为[L,R)。

#include<bits/stdc++.h>
using namespace std;
int main(){
int a[100]={1,2,3,3,4,5,5,6,8,9,22},v;
while(cin>>v){
int L=0,R=11;
while(L<R){
int M=(L+R)/2;
if(a[M]>=v) R=M;
else L=M+1;
}
cout<<L<<endl;
}
return 0;
}

注:1.当a[M]>=v时,由于a[R]是第一个大于等于v的数,那么R最大只能是M

  2.当a[M]<v时,说明[L,M)区间内的数都是小于v的,L作为最后的答案最小只能是M+1

2.第一个大于v

这就是我们常说的upper_bound()了,这是系统里面自带的库函数,下面是这个函数的原型:

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

其中first代表左边界迭代器,last代表右边界迭代器(注意左闭右开),val代表你要搜索的值,comp代表排序规则(这个参数在你对非结构体数组二分的时候并不需要,有默认规则)

实例:

int a[100]={1,2,3,3,4,5,5,6,8,9,22},n;
while(cin>>n){
int p=upper_bound(a,a+11,n)-a;
//如果a是vector,那么upper_bound(a.begin(),a.end(),v)-a.begin();
//你也可以在指定在[L,R)区间内二分upper_bound(a.begin()+L,a.begin()+R,v)-a.begin(),数组也是同理的
cout<<p<<endl;//这里输出的是第一个大于等于n的数的下标
}

接下来我们手写一个upper_bound()

#include<bits/stdc++.h>
using namespace std;
int main(){
int a[100]={1,2,3,3,4,5,5,6,8,9,22},v;
while(cin>>v){
int L=0,R=11;
while(L<R){
int M=(L+R)/2;
if(a[M]>v) R=M;
else L=M+1;
}
cout<<L<<endl;
}
return 0;
}

注:1.当a[M]>v时,由于a[R]是第一个大于v的数,那么R最大只能是M

  2.当a[M]<=v时,说明[L,M)区间内的下标都是小于等于v的,L作为最后的答案最小只能是M+1

3.最后一个小于等于v

当数组为降序的,使用lower_bound就是返回第一个小于等于下标,若一开始数组是升续的时候,那么应该先reverse一下,再用lower_bound返回下标p,则在原数组中的下标为n-p-1(假设数组有n个元素)。

这里来介绍一下如何在如果手写一个last_less_equal()。和lower_bound二分区间[L,R)左闭右开不同,last_less_equal()的二分区间为(L,R]右闭左开。

#include<bits/stdc++.h>
using namespace std;
int main(){
int a[100]={1,2,3,3,4,5,5,6,8,9,22},v;
while(cin>>v){
int L=-1,R=10;
while(L<R){
int M=(L+R+1)/2;
if(a[M]<=v) L=M;
else R=M-1;
}
cout<<L<<endl;
}
return 0;
}

注:1.当a[M]<=v时,由于a[L]是最后一个小于等于v的数,那么L最小只能是M。

  2.当a[M]>v时,说明(L,M]区间内的下标都是大于v的,R作为最后的答案最大只能是M-1。

4.最后一个小于v

 上面说过了,当数组为降序的,使用upper_bound就是返回第一个大于下标,若一开始数组是升续的时候,那么应该先reverse一下,再用upper_bound返回下标p,则在原数组中的下标为n-p-1(假设数组有n个元素)。

    这里来介绍一下如何在如果手写一个last_less()。和upper_bound二分区间[L,R)左闭右开不同,last_less_equal()的二分区间为(L,R]右闭左开。

#include<bits/stdc++.h>
using namespace std;
int main(){
int a[100]={1,2,3,3,4,5,5,6,8,9,22},v;
while(cin>>v){
int L=-1,R=10;
while(L<R){
int M=(L+R+1)/2;
if(a[M]<v) L=M;
else R=M-1;
}
cout<<L<<endl;
}
return 0;
}

注:1.当a[M]<n时,由于a[L]是最后一个小于v的数,那么L最小只能是M。

  2.当a[M]>=n时,说明(L,M]区间内的下标都是大于等于v的,R作为最后的答案最大只能是M-1。

我们发现lower_bound()和upper_bound()的M=(L+R)/2,而last_less()和last_less_equal()的M=(L+R+1)/2,(L+R)/2和(L+R+1)/2的区别在于前者是向下取整,后者是向上取整,这和我们定义L或者R是实际的答案有关。

二分搜素——(lower_bound and upper_bound)的更多相关文章

  1. 二分检索函数lower_bound()和upper_bound()

    二分检索函数lower_bound()和upper_bound() 一.说明 头文件:<algorithm> 二分检索函数lower_bound()和upper_bound() lower ...

  2. C++二分查找:lower_bound( )和upper_bound( )

    #include<algorithm>//头文件 //标准形式 lower_bound(int* first,int* last,val); upper_bound(int* first, ...

  3. 二分查找确定lower_bound和upper_bound

    lower_bound当target存在时, 返回它出现的第一个位置,如果不存在,则返回这样一个下标i:在此处插入target后,序列仍然有序. 代码如下: int lower_bound(int* ...

  4. 二分查找(lower_bound和upper_bound)

    转载自:https://www.cnblogs.com/luoxn28/p/5767571.html 1 二分查找 二分查找是一个基础的算法,也是面试中常考的一个知识点.二分查找就是将查找的键和子数组 ...

  5. LeetCode 34 - 在排序数组中查找元素的第一个和最后一个位置 - [二分][lower_bound和upper_bound]

    给定一个按照升序排列的整数数组 nums,和一个目标值 target.找出给定目标值在数组中的开始位置和结束位置. 你的算法时间复杂度必须是 O(log n) 级别. 如果数组中不存在目标值,返回 [ ...

  6. STL源码学习----lower_bound和upper_bound算法

    转自:http://www.cnblogs.com/cobbliu/archive/2012/05/21/2512249.html 先贴一下自己的二分代码: #include <cstdio&g ...

  7. [STL] lower_bound和upper_bound

    STL中的每个算法都非常精妙, ForwardIter lower_bound(ForwardIter first, ForwardIter last,const _Tp& val)算法返回一 ...

  8. lower_bound和upper_bound算法

    参考:http://www.cnblogs.com/cobbliu/archive/2012/05/21/2512249.html ForwardIter lower_bound(ForwardIte ...

  9. STL源码学习----lower_bound和upper_bound算法[转]

    STL中的每个算法都非常精妙,接下来的几天我想集中学习一下STL中的算法. ForwardIter lower_bound(ForwardIter first, ForwardIter last,co ...

随机推荐

  1. mysql 清空所有表数据重置自增ID

    1.登录mysql客户端 mysql -u root -p 输入密码进入 2.生成 sql 语句文件 SELECT CONCAT('TRUNCATE TABLE ',TABLE_NAME,';') i ...

  2. 摹客 · Veer 第二届设计大赛邀你来战!

    2018年12月,摹客设计大赛一年一度一归来. 继2017年摹客全国首届原型设计大赛成功举办后,本次大赛是摹客第二届设计大赛.大赛由摹客主办,Veer独家冠名赞助,iSlide和创客贴协办,国内多家知 ...

  3. git学习笔记:一台电脑上配置两个git账户

    如何在一台电脑上配置两个git账户,现在云端仓库很多,有开源中国的 gitee.com 微软的 github.com 还有 gitlab.com 和 bitbucket.org 等等,下面是具体步骤 ...

  4. 【算法专题】工欲善其事必先利其器—— 常用函数和STL

    一.    常用函数 #include <stdio.h> int getchar( void );               //读取一个字符, 一般用来去掉无用字符 char *ge ...

  5. Globalization and accessibility for tile and toast notifications (Windows Store apps)

    http://msdn.microsoft.com/en-us/library/windows/apps/hh831183.aspx 做 HighContrast时,采用以下分目录方式: /Proje ...

  6. NOIP水题测试(2017082401)

    哈,水题测试又来了! 上次的水题简单吧! 答案是以单题形式发布的(旅行家的预算随后发布). 下面来看今天的题,还是水题. 时间限制:5小时 题目一:看上去就很水 题目二:比上面一题还水 题目三:数的划 ...

  7. 论Java的重要性

    最近,最新的世界编程语言排名最近出炉了,Java位居世界第一.          不仅如此,Java以17.856%超过第二名C语言的8.726%两倍以上,其实,这一现象是十分反常的,因为,在前几年, ...

  8. ARM cortexM4中断优先级的一点理解。

    根据手册PM0214 40页.213页.200.195.interrupt priority grouping. 根据手册EM0090 第371页. stm32f42xxx除掉fpu部分,有91个可屏 ...

  9. TP5在lnmp环境中不能重写的问题

    说到坑,这个问题困扰了我一两天时间,本地可以,线上环境检查了好久. 基本检查的地方有几个了,首先就是nginx下面的重写配置,这个大家在网上都能搜到,至于定义的变量和配置路径,修改一下即可. 还有就是 ...

  10. 2018.12.08 codeforces 939E. Maximize!(二分答案)

    传送门 二分答案好题. 题意简述:要求支持动态在一个数列队尾加入一个新的数(保证数列单增),查询所有子数列的 最大值减平均值 的最大值. 然而网上一堆高人是用三分做的. 我们先考虑当前的答案有可能由什 ...