C++ upper_bound()和lower_bound()是涉及二分查找问题一个很好用的工具,熟练使用就不用为二分查找的边界发愁了(不用重复造轮子了)

1. 调用方式

upper_bound有两种调用方式:

template <class ForwardIterator, class T>
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val);
template <class ForwardIterator, class T, class Compare>
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val, Compare comp);

注意:

  1. 前两个参数是ForwardIterator 类型(这个一般比较容易满足,各种RandomAccessIterator都满足,而最常见的对vector排序,vector的迭代器就是RandomAccessIterator,参见:各种iterator之间的关系
  2. 如果采用自定义比较函数,传入的是对象而不是类名!!这个和构建一些有序的数据结构,如priority_queue,map,set等不一样,一般这些传入的是类名而不是一个对象!(举个例子,sort中的comp可以传入greater(),而不能是greater,见下文),其中涉及到函数对象的知识,可以参考:C++ 函数对象(Function Object)是什么?
    而对于构建priority_queue,这里传入的就是类名,而不是一个对象!
template<class T,class Container = std::vector<T>,class Compare = std::less<typename Container::value_type>
> class priority_queue;

同样的对于lower_bound来说也是如此:

template <class ForwardIterator, class T>
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val);
template <class ForwardIterator, class T, class Compare>
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val, Compare comp);

2. upper_bound()和lower_bound()定义和区别

定义

文档中的关于upper_bound()和lower_bound()的介绍值得深入细读:
以下来自官方文档:
upper_bound()

  1. 返回指向范围[first,last)中第一个元素使得value < element(或comp(value,element))为true(即严格大的迭代器),如果找不到这样的元素,则为last。
  2. [first,last)必须根据表达式!(value < element)或!comp(value,element)进行分区,即表达式为true的所有元素必须在表达式为false的所有元素之前!完全排序的[first,last)符合此条件。
  3. 如果没有自定义比较函数就使用operator<来比较element,如果自定义了比较函数就使用comp来比较element

lower_bound()

  1. 返回指向范围[first,last)中的第一个元素使得该元素不满足element< value(或comp(element,value))、(即大于或等于)的迭代器,如果找不到这样的元素,则返回last。

  2. [first,last)必须相对于表达式element< value(或comp(element,value))进行分区,即表达式为true的所有元素必须在表达式为false的所有元素之前。完全排序的[first,last)符合此条件。

  3. 如果没有自定义比较函数就使用operator<来比较element,如果自定义了比较函数就使用comp来比较element

2.1 lower_bound:

Returns an iterator pointing to the first element in the range [first, last) that does not satisfy element < value (or comp(element, value)), (i.e. greater or equal to), or last if no such element is found.

这里的value就是模板里的val,element就是要比较的元素

注意:在C++比较过程中,A less than B(A < B),表示按照“从小到大”(可以自定义)排序的话,A应该放在B前面
所以这里does not compare less than val就是greater或者equal to val的含义,也就是lower_bound返回 [first, last) 第一个不满足element < value(或者不满足comp(element, value))的元素的iterator,如果都不满足,则返回last这个iterator

注意:
1.

The range [first, last) must be partitioned with respect to the expression element < value (or comp(element, value)), i.e., all elements for which the expression is true must precede all elements for which the expression is false. A fully-sorted range meets this criterion.

也就是如果从大到小排序(greater),但是却用从小到大中以及最大的值作为比较函数,是查不到第一个元素的,而是返回last。
举例如下:

vector<int> vec = {1, 2, 3, 4, 5, 6};
// 从大到小排序
// greater必须加上()因为需要传入一个对象
sort(vec.begin(), vec.end(), greater<int>());
// 以默认的从小到大作为比较函数以及最大的值(此为6)作为value
auto iter = lower_bound(vec.begin(), vec.end(), 6);

这里返回的iter就是vec.end(),因为所有没有任何元素满足element < 6。
2. comp(element, value),第二个参数才是value!!

举例:对于自定义比较函数:

#include <iostream>     // std::cout
#include <algorithm>    // std::lower_bound
#include <vector>       // std::vector
using namespace std;
//以普通函数的方式定义查找规则
bool mycomp(int i,int j) { return i>j; }//以函数对象的形式定义查找规则
class mycomp2 {public:bool operator()(const int& i, const int& j) {return i > j;}
};int main() {int a[5] = { 1,2,3,4,5 };//从 a 数组中找到第一个不小于 3 的元素int *p = lower_bound(a, a + 5, 3);cout << "*p = " << *p << endl;vector<int> myvector{ 4,8, 9, 5,3,1,7, 2 };//根据 mycomp2 规则,从 myvector 容器中找到第一个违背 mycomp2 规则的元素vector<int>::iterator iter = lower_bound(myvector.begin(), myvector.end(),3,mycomp2());cout << "*iter = " << *iter;return 0;
}

输出的结果:

*p = 3
*iter = 3

在第二个例子中,3前面所有的元素都是大于3的,后面所有的元素都是小于3

2.2 upper_bound()

注意要点:

  1. comp(value,element),第一个参数就是value!!
    举例如下:
#include <iostream>     // std::cout
#include <algorithm>    // std::upper_bound
#include <vector>       // std::vector
using namespace std;
//以普通函数的方式定义查找规则
bool mycomp(int i, int j) { return i > j; }
//以函数对象的形式定义查找规则
class mycomp2 {public:bool operator()(const int& i, const int& j) {return i > j;}
};
int main() {int a[5] = { 1,2,3,4,5 };//从 a 数组中找到第一个大于 3 的元素int *p = upper_bound(a, a + 5, 3);cout << "*p = " << *p << endl;vector<int> myvector{ 4,5,3,1,2 };//根据 mycomp2 规则,从 myvector 容器中找到第一个违背 mycomp2 规则的元素vector<int>::iterator iter = upper_bound(myvector.begin(), myvector.end(), 3, mycomp2());cout << "*iter = " << *iter;return 0;
}

输出:

*p = 4
*iter = 1

在实际情况中,我们遇到的最多的就是给定从小到大排序好的数组,找出第一个大于某个元素或者大于等于某个元素的位置,找出第一个大于某个元素的位置就是用的upper_bound(),找出大于等于某个元素的位置就是lower_bound(),更复杂的情况就需要借助自定义的比较函数了。

参考资料:

  1. https://en.cppreference.com/w/cpp/algorithm/upper_bound
  2. https://en.cppreference.com/w/cpp/algorithm/lower_bound
  3. 代码案例部分来自于C语言中文网

C++ upper_bound()和lower_bound()(二分查找中使用)的定义,使用方法和区别相关推荐

  1. binary_search()、upper_bound()、lower_bound() 二分查找

    vector<int> a = {0,1,2,2,3,4}; 使用前提是a已经是升序排列 cout << binary_search(a.begin(), a.end(), 3 ...

  2. python类定义中__init__()_转:python学习——类中为什么要定义__init__()方法

    学习Python的类,一直不太理解为什么一定要定义init()方法,现在简要谈一下自己的理解吧. 1.不用init()方法定义类 定义一个矩形的类,目的是求周长和面积. 1 classRectangl ...

  3. java e.getmessage() null,浅谈Java异常的Exception e中的egetMessage()和toString()方法的区别...

    Exception e中e的getMessage()和toString()方法的区别: 示例代码1: public class TestInfo { private static String str ...

  4. python predict_对Keras中predict()方法和predict_classes()方法的区别说明

    1 predict()方法 当使用predict()方法进行预测时,返回值是数值,表示样本属于每一个类别的概率,我们可以使用numpy.argmax()方法找到样本以最大概率所属的类别作为样本的预测标 ...

  5. java——Scanner中nextLine()方法和next()方法的区别

    遇到一个有意思的东西,在整理字符串这块知识的时候,发现我在用Scanner函数时,在字符串中加入空格,结果空格后面的东西没有输出来(/尴尬),不多说直接上代码: import java.util.Sc ...

  6. android add fragment,fragment中的add和replace方法的区别浅析

    使用 FragmentTransaction 的时候,它提供了这样两个方法,一个 add , 一个 replace ,对这两个方法的区别一直有点疑惑. 我觉得使用 add 的话,在按返回键应该是回退到 ...

  7. Cesium 中两种添加 model 方法的区别

    概述 Cesium 中包含两种添加 model 的方法,分别为: 通过 viewer.entities.add() 函数添加 通过 viewer.scene.primitives.add() 函数添加 ...

  8. jquery中prop()方法和attr()方法的区别浅析

    引用:http://www.jb51.net/article/41170.htm 官方例举的例子感觉和attr()差不多,也不知道有什么区别,既然有了prop()这个新方法,不可能没用吧,那什么时候该 ...

  9. C# 中利用 Conditional 定义条件方法

    利用 Conditional 属性,程序员可以定义条件方法.Conditional 属性通过测试条件编译符号来确定适用的条件.当运行到一个条件方法调用时,是否执行该调用,要根据出现该调用时是否已定义了 ...

最新文章

  1. 如何禁止IIS缓存静态文件(png,js,html等)
  2. python有哪些软件包用来考察变量之间的相关性_Python计算数据相关系数(person、Kendall、spearman)...
  3. 爱回收:十年磨一剑,出鞘亮锋芒
  4. git 基本命令记录
  5. 《Head First设计模式》第七章-适配器模式、外观模式
  6. LeetCode 643. 子数组最大平均数 I
  7. style 字体加粗_第9篇 Qt Quick入门教程之基础(九)文本显示和字体
  8. 8-9 实现原理-1
  9. CentOS7中rpm,yum软件安装命令
  10. WPF中使用ItemsControl嵌套绑定,在ItemsControl中嵌套一个ItemsControl,然后使用绑定(2)...
  11. Hive Sql 安装
  12. Kali-简易的Linux系统Dos网站压力测试(攻击)
  13. [导入]WAP常见问题问答大全---七、关于WAP浏览器的常见问答
  14. 傅里叶级数 三角形式 到 复数形式
  15. 八大基本数据类型之基本类型与包装类型的区别
  16. idea自定义过滤器
  17. VC++实现电脑睡眠/休眠/锁定/关闭屏幕
  18. Qt读写Excel--QXlsx基本使用1
  19. hbs模块 mysql_让koa-hbs模块支持koa2
  20. 关于Windows10显示无法快速启动,查询日志显示:错误状态为 0xC00000D4的解决方案探索

热门文章

  1. c语言处理rna序列,RNAseq 完整操作流程以及后续例子操作
  2. 网页中链接中图片的下载
  3. EditPlus中文版下载
  4. js怎么实现对html代码加密解密,JS实现Base64加密解密
  5. win10修复计算机摁什么,win10修复引导工具怎么用?老司机教你使用win10修复引导工具...
  6. NeurIPS'22 | APG:面向CTR预估的自适应参数生成网络
  7. Oracle学习笔记:使用replace、regexp_replace实现字符替换、姓名脱敏
  8. 丰田将在所有销售店安装充电设备丰田章男社长“建立共享基础设施”
  9. eclipse 左侧导航栏不见怎么办
  10. 网络变压器在以太网中的作用