草庐IT

upper_bound和lower_bound用法(史上最全)

HuggingMe 2023-09-29 原文

目录


两者都是定义在头文件<algorithm>里。用 二分查找的方法在一个排好序的数组中进行查找。既然是二分,时间复杂度就是O(logN)。

基础用法

upper_bound(begin, end, value)

从小到大的排好序的数组中,在数组的 [begin, end) 区间中二分查找第一个大于value的数,找到返回该数字的地址,没找到则返回end。

lower_bound(begin, end, value)

从小到大的排好序的数组中,在数组的 [begin, end) 区间中二分查找第一个大于等于value的数,找到返回该数字的地址,没找到则返回end。

用greater<type>()重载

upper_bound(begin, end, value, greater<int>())

从大到小的排好序的数组中,在数组的 [begin, end) 区间中二分查找第一个小于value的数,找到返回该数字的地址,没找到则返回end。

lower_bound(begin, end, value, greater<int>())

从大到小的排好序的数组中,在数组的 [begin, end) 区间中二分查找第一个小于等于value的数,找到返回该数字的地址,没找到则返回end。

前两部分的代码:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
    // 1.基础用法
    vector<int> increasing = {1,2,3,4,5};
    int pos = upper_bound(increasing.begin(), increasing.end(), 3) - increasing.begin();
    cout << increasing[pos] << endl;   // 4(第一个大于3的数)
    pos = lower_bound(increasing.begin(), increasing.end(), 3) - increasing.begin();
    cout << increasing[pos] << endl;   // 3(第一个大于等于3的数)
    bool res = upper_bound(increasing.begin(), increasing.end(), 5) == increasing.end();
    cout << res << endl;               // 1 (true,即:第一个大于5的数不存在)
    // 2.greater重载
    vector<int> decreasing = {5,4,3,2,1};
    pos = upper_bound(decreasing.begin(), decreasing.end(), 3, greater<int>()) - decreasing.begin();
    cout << decreasing[pos] << endl;   // 2(第一个小于3的数)
    pos = lower_bound(decreasing.begin(), decreasing.end(), 3, greater<int>()) - decreasing.begin();
    cout << decreasing[pos] << endl;   // 3(第一个小于等于3的数)
    res = lower_bound(decreasing.begin(), decreasing.end(), 0, greater<int>()) == decreasing.end();
    cout << res << endl;               // 1 (true,即:第一个小于等于0的数不存在)
    return 0;
}

到这里还是比较好理解的,不加greater是大于/大于等于,加上greater就是小于/小于等于。upper_bound是不带等号的,lower_bound就是带等号的。一般博客到这里就结束了,如果想要更加灵活的使用,那么请接着看。

进阶用法(自定义匿名函数)

upper_bound进阶

upper_bound(begin, end, value, cmp)
bool cmp(value, element)

upper_bound的第四个参数是自定义的匿名函数cmp,返回值为bool类型,cmp有两个参数,一个是value,对,你没看错,就是upper_bound的第3个参数value,另一个是element,也就是查找过程中与value比较的那个数。upper_bound返回的就是[begin, end)区间中第一个满足cmp(value, element)为true的数。下面看两个例子,注意看注释,方便理解。

pos = upper_bound(increasing.begin(), increasing.end(), 3, [](int value, int element) -> bool {
    return value < element;
}) - increasing.begin();           // 等价于基础用法中的第1句
cout << increasing[pos] << endl;   // 4(value是3,第一个大于value的数)

其中[](int value, int element) -> bool {return value < element;}就是匿名函数,对于c++匿名函数的用法这里就不说了,自行百度。

pos = upper_bound(increasing.begin(), increasing.end(), 3, [](int value, int element) -> bool {
    return value <= element;
}) - increasing.begin();           // 等价于基础用法中的第2句
cout << increasing[pos] << endl;   // 3(value是3,第一个大于等于value的数)

没想到,upper_bound竟然用出了lower_bound的效果!这就是自定义函数的优点了,使用灵活,这里只是举一个例子展示一下,对于更复杂的情况,比如在一个有序的vector<vector<int>>中,想查找第一个满足第2个元素大于value的vector,匿名函数就可以写成:

[](int value, vector<int> element) -> bool {return value < element[1];}

lower_bound进阶

lower_bound(begin, end, value, cmp)
bool cmp(element, value)

注意,lower_bound的匿名函数element和value的顺序反过来了!lower_bound返回的是[begin, end)区间中第一个使cmp(element, value)为false的数。

pos = lower_bound(increasing.begin(), increasing.end(), 3, [](int element, int value) -> bool {
    return element < value;
}) - increasing.begin();           // 等价于基础用法中的第2句
cout << increasing[pos] << endl;   // 3(value是3,第一个大于等于value的数)
pos = lower_bound(increasing.begin(), increasing.end(), 3, [](int element, int value) -> bool {
    return element <= value;
}) - increasing.begin();           // 等价于基础用法中的第1句
cout << increasing[pos] << endl;   // 4(value是3,第一个大于value的数)

可以看出,lower_bound也可以用出upper_bound的效果。对于更复杂的情况,读者可以自己探索。

以上,任何一个用法都需要有序,否则无法进行二分查找。

所有代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    // 1.基础用法
    vector<int> increasing = {1,2,3,4,5};

    int pos = upper_bound(increasing.begin(), increasing.end(), 3) - increasing.begin();
    cout << increasing[pos] << endl;   // 4(第一个大于3的数)

    pos = lower_bound(increasing.begin(), increasing.end(), 3) - increasing.begin();
    cout << increasing[pos] << endl;   // 3(第一个大于等于3的数)

    bool res = upper_bound(increasing.begin(), increasing.end(), 5) == increasing.end();
    cout << res << endl;               // 1 (true,即:第一个大于5的数不存在)

    // 2.greater重载
    vector<int> decreasing = {5,4,3,2,1};

    pos = upper_bound(decreasing.begin(), decreasing.end(), 3, greater<int>()) - decreasing.begin();
    cout << decreasing[pos] << endl;   // 2(第一个小于3的数)

    pos = lower_bound(decreasing.begin(), decreasing.end(), 3, greater<int>()) - decreasing.begin();
    cout << decreasing[pos] << endl;   // 3(第一个小于等于3的数)

    res = lower_bound(decreasing.begin(), decreasing.end(), 0, greater<int>()) == decreasing.end();
    cout << res << endl;               // 1 (true,即:第一个小于等于0的数不存在)

    // 3.进阶用法
    pos = upper_bound(increasing.begin(), increasing.end(), 3, [](int value, int element) -> bool {
        return value < element;
    }) - increasing.begin();           // 等价于基础用法中的第1句
    cout << increasing[pos] << endl;   // 4(value是3,第一个大于value的数)

    pos = upper_bound(increasing.begin(), increasing.end(), 3, [](int value, int element) -> bool {
        return value <= element;
    }) - increasing.begin();           // 等价于基础用法中的第2句
    cout << increasing[pos] << endl;   // 3(value是3,第一个大于等于value的数)

    pos = lower_bound(increasing.begin(), increasing.end(), 3, [](int element, int value) -> bool {
        return element < value;
    }) - increasing.begin();           // 等价于基础用法中的第2句
    cout << increasing[pos] << endl;   // 3(value是3,第一个大于等于value的数)

    pos = lower_bound(increasing.begin(), increasing.end(), 3, [](int element, int value) -> bool {
        return element <= value;
    }) - increasing.begin();           // 等价于基础用法中的第1句
    cout << increasing[pos] << endl;   // 4(value是3,第一个大于value的数)
    
    return 0;
}

有关upper_bound和lower_bound用法(史上最全)的更多相关文章

  1. ruby - 有人可以解释一下在 Ruby 中注入(inject)的真实、通俗易懂的用法吗? - 2

    我正在学习Ruby,遇到了inject。我正处于理解它的风口浪尖,但当我是那种需要真实世界的例子来学习一些东西的人时。我遇到的最常见的例子是人们使用inject来添加一个(1..10)范围的总和,我不太关心这个。这是一个任意的例子。在实际程序中我会用它做什么?我正在学习,所以我可以继续使用Rails,但我不必有一个以Web为中心的示例。我只需要一些我可以全神贯注的目标。谢谢大家。 最佳答案 inject有时可以通过它的“其他”名称reduce更好地理解。它是一个对Enumerable进行操作(迭代一次)并返回单个值的函数。它有许多有

  2. ruby - 使用法拉第上传文件 - 2

    我在尝试使用Faraday将文件上传到网络服务时遇到问题。我的代码:conn=Faraday.new('http://myapi')do|f|f.request:multipartendpayload={:file=>Faraday::UploadIO.new('...','image/jpeg')}conn.post('/',payload)尝试发布后似乎没有任何反应。当我检查响应时this是我所看到的:#:post,:body=>#,#,@opts={}>,#],@index=0>>,#>],@ios=[#,#,@opts={}>,#],@index=0>,#],@index=0>

  3. ruby - rspec: raise_error 用法来匹配错误信息 - 2

    我使用raise(ConfigurationError.new(msg))引发错误我试着用rspec测试一下:expect{Base.configuration.username}.toraise_error(ConfigurationError,message)但这行不通。我该如何测试呢?目标是匹配message。 最佳答案 您可以使用正则表达式匹配错误消息:it{expect{Foo.bar}.toraise_error(NoMethodError,/private/)}这将检查NoMethodError是否由privateme

  4. 【ChatGPT】ChatGPT 的 N 种用法 - 2

    目录ChatGPT简介技术原理应用未来发展ChatGPT的10 种用法ChatGPT简介ChatGPT是一种基于深度学习的大型语言模型,由OpenAI公司开发。技术原理GPT是GenerativePre-trainedTransformer的缩写,意为生成式预训练变压器。它的技术原理是使用了一个基于注意力机制的变压器(Trans

  5. ruby - 是否有 Rack::Session::Cookie 用法的基本示例? - 2

    我找不到任何使用Rack::Session::Cookie的简单示例,并且希望能够将信息存储在cookie中,并在以后的请求中访问它并让它过期.这些是我能找到的唯一示例:HowdoIset/getsessionvarsinaRackapp?http://rack.rubyforge.org/doc/classes/Rack/Session/Cookie.html这是我得到的:useRack::Session::Cookie,:key=>'rack.session',:domain=>'foo.com',:path=>'/',:expire_after=>2592000,:secret=

  6. ruby - Ruby 方法的双冒号(双列或::)语法的惯用用法 - 2

    我是Ruby的新手,发现以下几对令人困惑示例同样有效:File.included_modulesFile::included_modulesFile.stat('mbox')#Returnsa'#'objectFile::stat('mbox')File.new("foo.txt","w")File::new("foo.txt","w")"asdf".size#Aninstancemethod"asdf"::size2+32::send(:+,3)#AnextremeexampleFile::new,尤其是我经常遇到的东西。我的问题:如果我永远避免使用::运算符来限定除类、模块和常量之

  7. ruby-on-rails - 如果只有一个存在,是否有用于返回第一个数组元素的 ruby​​ 习惯用法? - 2

    如果数组只包含一个值,我想返回数组的第一个元素。目前,我使用:vals.one??vals.first:vals.presence因此:vals=[];vals.one??vals.first:vals.presence#=>nilvals=[2];vals.one??vals.first:vals.presence#=>2vals=[2,'Z'];vals.one??vals.first:vals.presence#=>[2,"Z"]是否有内置的东西可以做到这一点,或者是否有更好的设计考虑?我的用例是特定的,涉及知道从方法(将实现上述代码)中期望什么的演示者。如果这些演示者将所有返回

  8. ruby - 这是 &&= 在 Ruby 中的合理用法吗? - 2

    在SOquestion2068165一个答案提出了使用这样的东西的想法:params[:task][:completed_at]&&=Time.parse(params[:task][:completed_at])作为DRYer的说法params[:task][:completed_at]=Time.parse(params[:task][:completed_at])ifparams[:task][:completed_at]paramsHash将来自(Rails/ActionView)表单。这是众所周知的||=习语的一种推论,如果LHS不是nil/false则设置值。像这样使用&&

  9. ruby - Head 用法未知选项 -1/-n 错误。可能与 ruby 有关 - 2

    我在OSX10.9.1中启动终端时反复出现问题。每次启动终端时,我都会重复以下至少30次Unknownoption:1Usage:head[-options]...-musemethodfortherequest(defaultis'HEAD')-fmakerequestevenifheadbelievesmethodisillegal-bUsethespecifiedURLasbase-tSettimeoutvalue-iSettheIf-Modified-Sinceheaderontherequest-cusethiscontent-typeforPOST,PUT,CHECKIN-

  10. ruby-on-rails - rails 中是否内置了对默认值替换习惯用法的支持? - 2

    我经常编写代码以在遇到nil/空值时提供默认值。例如:category=order.category||"Any"#ORcategory=order.category.empty??"Any":order.category我即将扩展try方法来处理这个习语。category=order.try(:category,:on_nill=>"Any")#ORcategory=order.try(:category,:on_empty=>"Any")我想知道Rails/Ruby是否有一些方法来处理这个习惯用法?注意:我正在尝试消除||的重复/或/?基于运算符的习语。本质上,我正在寻找与try方

随机推荐