当前位置: 首页 > 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.jsqmd.com/news/1099151/

相关文章:

  • 当DXSL 系列矢量信号源遇上高空风机,电磁测试不再需要 “负重前行”
  • Windows系统文件AppInstallerPrompt.Desktop.dll丢失找不到问题解决
  • 第三视觉理解徐玉生与他的商业活动(14)
  • TwitchDropsMiner:无需观看直播,自动化获取Twitch掉落奖励的终极指南
  • 抖音下载器:一键保存无水印视频,轻松构建个人数字内容库
  • TVA与具身智能深度融合的内在必然性(6)
  • Coze平台多智能体工作流实战:从零构建智能开发助手
  • phytium-kernel性能调优手册:飞腾处理器内核参数优化与性能测试终极指南
  • utcpio社区生态:参与openEuler开源项目的完整指南
  • 计算机毕业设计之高校防疫系统
  • 别再手动拼矩阵了!用MATLAB的triu和tril函数,5分钟搞定随机对称矩阵生成
  • FAE放射组学分析工具:医学影像特征探索的完整解决方案
  • Firefly ITX-RK3588开发板实战:从MIPI CSI摄像头采集到GStreamer UDP推流,保姆级避坑指南
  • 【JAVA毕设源码分享】基于springboot电影院票务预定系统的设计与实现(程序+文档+代码讲解+一条龙定制)
  • 如何通过CXPatcher终极补丁工具快速提升Mac游戏兼容性?
  • 5分钟掌握B站会员购抢票神器:告别手速焦虑的终极指南
  • 数据分析师必学MySQL:从零构建电商销售分析实战
  • YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践
  • Dify 本地部署与 AI 应用开发实战:从零构建智能工作流
  • 终极开源音乐播放器指南:MoeKoe Music让酷狗音乐体验焕然一新
  • DesktopNaotu:你的终极离线思维导图解决方案,告别网络依赖!
  • 版本兼容设计事件类预留版本字段:
  • 【Springboot毕设全套源码+文档】基于Java+springboot二手滑板交易系统的设计与实现(丰富项目+远程调试+讲解+定制)
  • 计算机Java毕设实战-基于 SpringBoot 的大学生在线评教打分系统的设计与实现 基于 SpringBoot 的高校教学质量评价系统【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • CryptoHack Writeup——Stream of Consciousness:流密码密钥复用漏洞分析
  • biliTickerBuy:B站会员购抢票工具的终极指南与实战技巧
  • HS2-HF Patch:3步实现HoneySelect2完美汉化与MOD整合
  • 第三视觉理解徐玉生与他的商业活动(12)
  • Coze与Dify对比指南:低代码AI应用开发从入门到实战
  • Agentic AI 复利效应:从自动化到经验积累的智能体系统设计