当前位置: 首页 > news >正文

别再死记硬背了!用‘分界线’思维彻底搞懂C++ set的lower_bound和upper_bound

用‘分界线’思维彻底掌握C++ set的lower_bound和upper_bound

在C++标准模板库(STL)中,set容器因其自动排序和快速查找的特性而广受欢迎。然而,许多初学者在使用lower_boundupper_bound这两个关键方法时,常常陷入死记硬背"大于"或"大于等于"的泥潭,导致在实际应用中频频出错。本文将带你跳出传统记忆法的局限,从"分界线"这一核心概念出发,建立直观且稳固的理解模型。

1. 为什么需要分界线思维

传统记忆法通常将lower_boundupper_bound简单定义为:

  • lower_bound: 返回第一个大于等于给定值的元素
  • upper_bound: 返回第一个大于给定值的元素

这种定义在升序排列的set中看似合理,但当容器采用降序排列或自定义排序规则时,就会导致混乱。更本质的理解应该是:这两个方法实际上是在有序序列中划分分界线,而分界线的位置取决于容器的排序规则。

考虑一个简单的例子:

set<int> ascending = {1, 3, 5, 7, 9}; // 默认升序 set<int, greater<int>> descending = {9, 7, 5, 3, 1}; // 降序

对于值4,在不同排序规则下:

方法升序结果降序结果
lower_bound53
upper_bound53

表:相同值在不同排序规则下的查找结果对比

2. 分界线的直观理解

想象你要在一排有序的书架上插入一本新书。lower_boundupper_bound就是在告诉你:如果要保持书架有序,这本新书可以插入的最左和最右位置。

2.1 升序排列的分界线

对于升序set,分界线可以这样理解:

  1. lower_bound(val): 第一个可以插入val而不破坏顺序的位置
  2. upper_bound(val): 最后一个可以插入val而不破坏顺序的位置的下一个位置
set<int> s = {1, 3, 5, 7, 9}; // 插入分界线演示 auto lb = s.lower_bound(4); // 指向5 auto ub = s.upper_bound(4); // 同样指向5

代码:升序set中分界线的位置

2.2 降序排列的分界线

降序排列时,逻辑完全镜像:

set<int, greater<int>> s = {9, 7, 5, 3, 1}; // 插入分界线演示 auto lb = s.lower_bound(4); // 指向3 auto ub = s.upper_bound(4); // 同样指向3

代码:降序set中分界线的位置

3. 分界线与区间遍历

理解了分界线概念后,我们可以轻松实现各种区间遍历需求:

3.1 升序set的区间遍历

set<int> s = {1, 3, 5, 7, 9}; // 遍历所有 >=4 的元素 for(auto it = s.lower_bound(4); it != s.end(); ++it) { cout << *it << " "; // 输出: 5 7 9 } // 遍历所有 >4 的元素 for(auto it = s.upper_bound(4); it != s.end(); ++it) { cout << *it << " "; // 输出: 5 7 9 }

3.2 降序set的区间遍历

set<int, greater<int>> s = {9, 7, 5, 3, 1}; // 遍历所有 >=4 的元素 for(auto it = s.begin(); it != s.upper_bound(4); ++it) { cout << *it << " "; // 输出: 9 7 5 3 } // 遍历所有 >4 的元素 for(auto it = s.begin(); it != s.lower_bound(4); ++it) { cout << *it << " "; // 输出: 9 7 5 }

4. 实际应用中的注意事项

  1. 自定义比较函数的影响:当使用自定义比较函数时,分界线的划分完全取决于比较函数的定义方式。

  2. 元素不存在的情况:当查找的值大于(升序)或小于(降序)所有元素时,两个方法都返回end()

  3. 性能考虑:这两个方法的时间复杂度都是O(log n),在大型集合中依然保持高效。

  4. 与equal_range的关系equal_range返回的是一个区间,相当于make_pair(lower_bound, upper_bound)

// 使用equal_range的示例 auto range = s.equal_range(5); for(auto it = range.first; it != range.second; ++it) { cout << *it << " "; // 输出所有等于5的元素 }

代码:equal_range的使用示例

5. 分界线思维的扩展应用

理解了分界线概念后,这一思维可以扩展到其他STL容器和算法:

  1. multiset:允许重复元素,但分界线概念同样适用
  2. map/multimap:基于键值排序,分界线根据键值划分
  3. 排序算法:如partition等算法也涉及分界线概念

在实际项目中,我曾遇到需要统计某个分数段内学生人数的需求。使用分界线思维,代码变得异常简洁:

set<int> scores = {55, 60, 65, 70, 75, 80, 85, 90, 95}; // 统计70-85分的学生人数 auto low = scores.lower_bound(70); auto high = scores.upper_bound(85); int count = distance(low, high); // 结果为5

代码:实际项目中的应用示例

http://www.gsyq.cn/news/1611118.html

相关文章:

  • 计算机毕业设计之高校防疫系统
  • utcpio社区生态:参与openEuler开源项目的完整指南
  • Firefly ITX-RK3588开发板实战:从MIPI CSI摄像头采集到GStreamer UDP推流,保姆级避坑指南
  • 别再手动拼矩阵了!用MATLAB的triu和tril函数,5分钟搞定随机对称矩阵生成
  • 【JAVA毕设源码分享】基于springboot电影院票务预定系统的设计与实现(程序+文档+代码讲解+一条龙定制)
  • DesktopNaotu:你的终极离线思维导图解决方案,告别网络依赖!
  • Dify 本地部署与 AI 应用开发实战:从零构建智能工作流
  • 数据分析师必学MySQL:从零构建电商销售分析实战
  • 第三视觉理解徐玉生与他的商业活动(12)
  • CryptoHack Writeup——Stream of Consciousness:流密码密钥复用漏洞分析
  • 计算机Java毕设实战-基于 SpringBoot 的大学生在线评教打分系统的设计与实现 基于 SpringBoot 的高校教学质量评价系统【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 基于BouncyCastle实现TLCP国密协议Java客户端实战指南
  • 三步完成iOS激活锁绕过:applera1n免费解锁iPhone 6s-X终极指南
  • 别再乱按复位键了!手把手教你搞懂STM32的三种复位方式(含独立/窗口看门狗详解)
  • 3步实现专业直播抠像:obs-backgroundremoval AI背景移除插件终极指南
  • 【C++】内存空间理解
  • 基于Dify与DeepSeek构建私有知识库问答系统实战指南
  • 第五期:合法工具的武器化 —— 披着羊皮的狼 (Living off the Land)
  • AI生图工具怎么选?2026年6月版实测对比
  • 【AI大模型应用开发】【项目实战】9.基于GPT2搭建医疗问诊机器人
  • Java开发者实战指南:Spring Boot集成AI大模型与Agent开发
  • Domain3-2 安全模型
  • Mac与Android无缝连接:HoRNDIS USB网络共享驱动深度解析
  • 2026年6月零代码网站搭建与企业无代码建站工具测评:谁更适合你
  • 解决音频格式兼容性难题:FlicFlac轻量级音频转换工具深度解析
  • 餐饮老板必看:扫码点餐小程序3步搞定,别再让顾客干等了!
  • 抖音内容监控助手:告别手动刷新,让优质内容主动找你
  • 移动端游戏功耗测试实战:电流、功率、亮度和场景对比
  • 足球口袋教练 HarmonyOS 离线应用实战(03/20):ArkUI 首页仪表盘搭建
  • [C++]内存管理:串顺序存储的内存回收