STL源码学习---lower_bound和upper_bound

STL lower_bound与upper_bound详解
本文详细介绍了STL中的lower_bound和upper_bound算法,包括它们的功能、使用场景及其实现方式。lower_bound用于找到非递减序列中第一个大于等于指定值的位置,而upper_bound则用于找到第一个大于指定值的位置。

转载自:http://www.cnblogs.com/cobbliu/archive/2012/05/21/2512249.html

             http://blog.youkuaiyun.com/niushuai666/article/details/6734403

             http://blog.youkuaiyun.com/niushuai666/article/details/6734650

所包含头文件:#include <algorithm>

STL中的每个算法都非常精妙,接下来的几天我想集中学习一下STL中的算法。

  ForwardIter lower_bound(ForwardIter first, ForwardIter last,const _Tp& val)算法返回一个非递减序列[first, last)中的第一个大于等于值val的位置。

     ForwardIter upper_bound(ForwardIter first, ForwardIter last, const _Tp& val)算法返回一个非递减序列[first, last)中第一个大于val的位置。

     lower_bound和upper_bound如下图所示:

 

1, lower_bound

函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置

举例如下:

一个数组number序列为:4,10,11,30,69,70,96,100.设要插入数字3,9,111.pos为要插入的位置的下标

pos = lower_bound( number, number + 8, 3) - number,pos = 0.即number数组的下标为0的位置。

pos = lower_bound( number, number + 8, 9) - number, pos = 1,即number数组的下标为1的位置(即10所在的位置)。

pos = lower_bound( number, number + 8, 111) - number, pos = 8,即number数组的下标为8的位置(但下标上限为7,所以返回最后一个元素的下一个元素)。

所以,要记住:函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置,且last的位置是越界的!!~

返回查找元素的第一个可安插位置,也就是“元素值>=查找值”的第一个元素的位置


  这个序列中可能会有很多重复的元素,也可能所有的元素都相同,为了充分考虑这种边界条件,STL中的lower_bound算法总体上是才用了二分查找的方法,但是由于是查找序列中的第一个出现的值大于等于val的位置,所以算法要在二分查找的基础上做一些细微的改动。

     首先是我修改数据结构课本上的二分查找实现的lower_bound算法:

int my_lower_bound(int *array, int size, int key)
{
    int first = 0, last = size-1;
    int middle, pos=0;       //需要用pos记录第一个大于等于key的元素位置

    while(first < last)
    {
        middle = (first+last)/2;
        if(array[middle] < key){      //若中位数的值小于key的值,我们要在右边子序列中查找,这时候pos可能是右边子序列的第一个
            first = middle + 1;
            pos = first;
        }
        else{
            last = middle;           //若中位数的值大于等于key,我们要在左边子序列查找,但有可能middle处就是最终位置,所以我们不移动last,
            pos = last;              //而是让first不断逼近last。
        }
    }
    return pos;
}

STL中的实现比较精巧,下面贴出源代码:

//这个算法中,first是最终要返回的位置
int lower_bound(int *array, int size, int key)
{
    int first = 0, middle;
    int half, len;
    len = size;

    while(len > 0) {
        half = len >> 1;
        middle = first + half;
        if(array[middle] < key) {     
            first = middle + 1;          
            len = len-half-1;       //在右边子序列中查找
        }
        else
            len = half;            //在左边子序列(包含middle)中查找
    }
    return first;
}

2, upper_bound

函数upper_bound()返回的在前闭后开区间查找的关键字的上界,如一个数组number序列1,2,2,4.upper_bound(2)后,返回的位置是3(下标)也就是4所在的位置,同样,如果插入元素大于数组中全部元素,返回的是last。(注意:此时数组下标越界!!)

返回查找元素的最后一个可安插位置,也就是“元素值>查找值”的第一个元素的位置

upper_bound返回的是最后一个大于等于val的位置,也是有一个新元素val进来时的插入位置。

我依然将二分查找略做修改:

int my_upper_bound(int *array, int size, int key)
{
    int first = 0, last = size-1;
    int middle, pos = 0;

    while(first < last)
    {
        middle = (first+last)/2;
        if(array[middle] > key){     //当中位数大于key时,last不动,让first不断逼近last
            last = middle;
            pos = last;
        }
        else{
            first = middle + 1;     //当中位数小于等于key时,将first递增,并记录新的位置
            pos = first;
        }
    }
    return pos;
}

下面的代码是STL中的upper_bound实现:

int upper_bound(int *array, int size, int key)
{
    int first = 0, len = size-1;
    int half, middle;

    while(len > 0){
        half = len >> 1;
        middle = first + half;
        if(array[middle] > key)     //中位数大于key,在包含last的左半边序列中查找。
            len = half;
        else{
            first = middle + 1;    //中位数小于等于key,在右半边序列中查找。
            len = len - half - 1;
        }
    }
    return first;
}


(1)普通用户端(全平台) 音乐播放核心体验: 个性化首页:基于 “听歌历史 + 收藏偏好” 展示 “推荐歌单(每日 30 首)、新歌速递、相似曲风推荐”,支持按 “场景(通勤 / 学习 / 运动)” 切换推荐维度。 播放页功能:支持 “无损音质切换、倍速播放(0.5x-2.0x)、定时关闭、歌词逐句滚动”,提供 “沉浸式全屏模式”(隐藏冗余控件,突出歌词与专辑封面)。 多端同步:自动同步 “播放进度、收藏列表、歌单” 至所有登录设备(如手机暂停后,电脑端打开可继续播放)。 音乐发现与管理: 智能搜索:支持 “歌曲名 / 歌手 / 歌词片段” 搜索,提供 “模糊匹配(如输入‘晴天’联想‘周杰伦 - 晴天’)、热门搜索词推荐”,结果按 “热度 / 匹配度” 排序。 歌单管理:创建 “公开 / 私有 / 加密” 歌单,支持 “批量添加歌曲、拖拽排序、一键分享到社交平台”,系统自动生成 “歌单封面(基于歌曲风格配色)”。 音乐分类浏览:按 “曲风(流行 / 摇滚 / 古典)、语言(国语 / 英语 / 日语)、年代(80 后经典 / 2023 新歌)” 分层浏览,每个分类页展示 “TOP50 榜单”。 社交互动功能: 动态广场:查看 “关注的用户 / 音乐人发布的动态(如‘分享新歌感受’)、好友正在听的歌曲”,支持 “点赞 / 评论 / 转发”,可直接点击动态中的歌曲播放。 听歌排行:个人页展示 “本周听歌 TOP10、累计听歌时长”,平台定期生成 “全球 / 好友榜”(如 “好友中你本周听歌时长排名第 3”)。 音乐圈:加入 “特定曲风圈子(如‘古典音乐爱好者’)”,参与 “话题讨论(如‘你心中最经典的钢琴曲’)、线上歌单共创”。 (2)音乐人端(创作者中心) 作品管理: 音乐上传:支持 “无损音频(FLAC/WAV)+ 歌词文件(LRC)+ 专辑封面” 上传,填写 “歌曲信息
<think>首先,用户的问题是:“我想查找lower_boundupper_bound的实现方法或使用示例 请问C++ lower_bound upper_bound 的用法实现”。系统指令要求我使用中文回答,结构清晰,逐步解决问题,并保证回答真实可靠。 系统指令还指定了格式: - 行内数学表达式使用$...$,例如 $x^2$。 - 独立公式使用$$...$$并单独成段。 - LaTeX语法正确。 - 生成相关问题。 - 在引用的段落末尾添加引用标识,如[^1]。 参考的站内引用: - 引用[1]:提到setmap有自己的lower_boundupper_bound,效率高于STL中的通用版本。 - 引用[2]:给出了lower_bound的实现代码。 - 引用[3]:展示了std::lower_boundstd::upper_bound在map中的使用示例。 - 引用[4]:解释了lower_boundupper_bound的常见用法,包括参数返回值。 我需要基于这些引用构建回答。用户要求的是lower_boundupper_bound的用法实现方法。 结构化回答: 1. **引言**:简要介绍lower_boundupper_bound是什么。 2. **用法**:解释如何在C++中使用它们,包括在vectormap中的示例。 3. **实现方法**:提供或描述算法的实现。 4. **示例**:给出代码示例。 5. **相关问题**:生成相关问题。 确保回答真实可靠:基于标准C++库的知识,引用提供的资料。 关键点: - lower_bound:返回第一个大于或等于给定值的元素的迭代器。 - upper_bound:返回第一个大于给定值的元素的迭代器。 - 在有序容器中使用,如vector、set、map。 从引用[2],有lower_bound的实现代码: ```c int lower_bound(int *array, int size, int val) { int first = 0; int len = size; while (len > 0) { int half = len >> 1; int middle = first + half; if (array[middle] < val) { first = middle + 1; len = len - half - 1; } else { len = half; } } return first; } ``` 这是一个二分查找的实现。 从引用[3],有map中的使用示例: ```c++ #include <iostream> #include <map> int main() { std::map<char, int> mymap; std::map<char, int>::iterator itlow, itup; mymap['a'] = 20; mymap['b'] = 40; mymap['c'] = 60; mymap['d'] = 80; mymap['e'] = 100; itlow = mymap.lower_bound('b'); // points to 'b' itup = mymap.upper_bound('d'); // points to 'e' mymap.erase(itlow, itup); // erases from 'b' to 'd' for (auto it = mymap.begin(); it != mymap.end(); ++it) std::cout << it->first << " => " << it->second << '\n'; return 0; } ``` 输出应为ae。 从引用[4],lower_bound的用法描述:在数组中找到第一个大于或等于num的元素,返回地址或下标。 生成相关问题: 1. lower_boundupper_bound在无序容器中能用吗? 2. 如何用lower_bound实现二分查找? 3. 在自定义比较函数时如何使用它们? 回答结构: - 先解释概念。 - 提供用法。 - 展示实现。 - 给出示例。 - 添加相关问题。 确保使用中文。</think>### C++ `lower_bound` `upper_bound` 的用法与实现 在 C++ 标准库中,`lower_bound` `upper_bound` 是用于有序序列(如数组、`vector`、`set`、`map`)的高效二分查找算法。它们基于二分查找原理,时间复杂度为 $O(\log n)$。下面我将从用法、实现方法示例三个方面详细解释,确保内容真实可靠(基于 C++ 标准常见实践)。 --- #### 1. **核心概念用法** - **`lower_bound`**:返回序列中第一个**大于或等于**给定值的元素的迭代器(或下标)。如果值不存在,返回指向序列末尾的迭代器。 数学描述:在有序序列 $[begin, end)$ 中,找到最小索引 $i$ 满足 $a_i \geq val$。 - **`upper_bound`**:返回序列中第一个**大于**给定值的元素的迭代器(或下标)。如果值不存在,返回序列末尾。 数学描述:在有序序列 $[begin, end)$ 中,找到最小索引 $i$ 满足 $a_i > val$。 - **适用容器**:需有序序列,如 `std::vector`, `std::set`, `std::map`。对于关联容器(如 `set` `map`),优先使用其成员函数版本(`set.lower_bound(val)`),效率更高,因为直接利用内部树结构[^1]。 - **参数格式**: ```cpp // 通用版本(适用于 vector 等) iterator lower_bound(iterator begin, iterator end, const T& val); iterator upper_bound(iterator begin, iterator end, const T& val); // 成员函数版本(适用于 set/map) set.lower_bound(val); // 返回 set 的迭代器 map.lower_bound(key); // 返回 map 的迭代器 ``` - **返回值**:迭代器或下标。通过 `std::distance(begin, iter)` 可获取下标。 --- #### 2. **实现方法(基于二分查找)** 这两个函数的底层实现是二分查找。以下是 `lower_bound` 的经典实现(基于引用[2]的代码,优化为 C++ 风格): ```cpp // lower_bound 实现:在有序数组 array 中查找 val template <typename T> int lower_bound(T* array, int size, T val) { int first = 0; int len = size; while (len > 0) { int half = len >> 1; // 等价于 len / 2 int middle = first + half; if (array[middle] < val) { first = middle + 1; len = len - half - 1; // 调整剩余长度 } else { len = half; // 调整剩余长度 } } return first; // 返回下标 } ``` - **算法原理**: - 初始化 `first=0` `len=size`,表示搜索区间 $[first, first+len)$。 - 循环中,计算中点 `middle`。如果中点值小于 `val`,则搜索右半区间;否则搜索左半区间。 - 最终返回 `first`,即第一个 $\geq val$ 的元素下标。 - **`upper_bound` 实现**:类似 `lower_bound`,但条件改为 `if (array[middle] <= val)`,确保找到第一个 $> val$ 的元素。 - **数学保证**:二分查找确保时间复杂度为 $O(\log n)$,空间复杂度 $O(1)$。 --- #### 3. **使用示例** 下面通过 `vector` `map` 的示例展示具体用法(基于引用[3]引用[4])。 **示例 1:在 vector 中查找下标** ```cpp #include <iostream> #include <vector> #include <algorithm> // 需要包含此头文件 int main() { std::vector<int> vec = {10, 20, 30, 30, 40, 50}; int val = 30; // lower_bound:返回第一个 >=30 的元素迭代器 auto low = std::lower_bound(vec.begin(), vec.end(), val); int low_idx = std::distance(vec.begin(), low); // 下标计算 std::cout << "lower_bound 下标: " << low_idx << " (值: " << *low << ")\n"; // 输出: 下标: 2 (值: 30) // upper_bound:返回第一个 >30 的元素迭代器 auto up = std::upper_bound(vec.begin(), vec.end(), val); int up_idx = std::distance(vec.begin(), up); std::cout << "upper_bound 下标: " << up_idx << " (值: " << *up << ")\n"; // 输出: 下标: 4 (值: 40) // 应用:删除所有值为 30 的元素 vec.erase(std::lower_bound(vec.begin(), vec.end(), 30), std::upper_bound(vec.begin(), vec.end(), 30)); for (int x : vec) std::cout << x << " "; // 输出: 10 20 40 50 return 0; } ``` **示例 2:在 map 中使用成员函数(高效版)** ```cpp #include <iostream> #include <map> int main() { std::map<char, int> mymap; mymap['a'] = 20; mymap['b'] = 40; mymap['c'] = 60; mymap['d'] = 80; mymap['e'] = 100; // 使用 map 的成员函数 lower_bound/upper_bound auto itlow = mymap.lower_bound('b'); // 指向 'b' (键值 >= 'b') auto itup = mymap.upper_bound('d'); // 指向 'e' (键值 > 'd') // 删除区间 [itlow, itup)(即 'b' 到 'd') mymap.erase(itlow, itup); // 输出剩余元素 for (auto it = mymap.begin(); it != mymap.end(); ++it) std::cout << it->first << " => " << it->second << '\n'; // 输出: a => 20, e => 100 return 0; } ``` - **关键点**: - 在 `map` 中,`lower_bound('b')` 返回指向键 `'b'` 的迭代器,`upper_bound('d')` 返回指向 `'e'` 的迭代器(因为 `'e'` 是第一个大于 `'d'` 的键)[^3]。 - 成员函数版本比通用 `std::lower_bound` 更快,因为它直接利用红黑树结构[^1]。 --- #### 注意事项 - **序列必须有序**:使用前确保容器已排序,否则行为未定义。 - **自定义比较**:支持传入比较函数,如 `std::lower_bound(vec.begin(), vec.end(), val, custom_comp)`。 - **性能**:二分查找确保高效,但频繁插入/删除时,优先选择 `set/map`。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值