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

C++中的 lower_bound 和 upper_bound:一篇讲清楚

在刷题或者写代码的时候,lower_boundupper_bound是两个非常高频但又容易用错的函数。很多人知道“能用”,但不知道“为什么这样用”。

这篇文章不搞花里胡哨的定义,直接从实际理解、使用方式、常见坑这几个角度讲清楚。


这两个函数到底是干嘛的;一句话版本

在一个有序序列中:

  • lower_bound:找到第一个 >= x 的位置

  • upper_bound:找到第一个 > x 的位置

记住这句话基本就够用了。


举个最简单的例子

vector<int> v = {1, 2, 2, 2, 3, 4};

查找2

lower_bound(v.begin(), v.end(), 2) → 指向第一个 2 upper_bound(v.begin(), v.end(), 2) → 指向第一个 3

也就是说:

lower_bound → 左边界 upper_bound → 右边界

返回的是什么;别搞错了

这两个函数返回的不是下标,而是:

迭代器(iterator)

如果你要下标,需要这样写:

int pos = lower_bound(v.begin(), v.end(), x) - v.begin();

最常见用途一;判断元素是否存在

auto it = lower_bound(v.begin(), v.end(), x); if (it != v.end() && *it == x) { cout << "存在"; }

解释:

  • lower_bound 找到第一个 >= x 的位置

  • 如果这个位置刚好等于 x → 说明存在


最常见用途二;统计某个数出现次数

int cnt = upper_bound(v.begin(), v.end(), x) - lower_bound(v.begin(), v.end(), x);

这行代码非常经典,直接记住:

出现次数 = 右边界 - 左边界


最常见用途三;插入位置

如果你要往有序数组中插入一个元素:

auto it = lower_bound(v.begin(), v.end(), x); v.insert(it, x);

这样插入之后,数组依然有序。


必须注意的前提;一定要有序

这两个函数的前提是:

序列必须是有序的(默认升序)

如果是乱序数组:

  • 结果是错的

  • 但不会报错(这是最坑的地方)


自定义排序怎么办

如果你用的是自定义排序,比如降序:

sort(v.begin(), v.end(), greater<int>());

那你就必须传入同样的比较函数:

lower_bound(v.begin(), v.end(), x, greater<int>());

否则结果是错误的。


手写理解;它本质就是二分

可以用一个简单的“手写版本”来理解 lower_bound:

int l = 0, r = n; while (l < r) { int mid = (l + r) / 2; if (v[mid] >= x) r = mid; else l = mid + 1; }

最终:

l 就是 lower_bound 的位置

而 upper_bound 只是把条件改成:

v[mid] > x

常见坑总结

; 忘了数组必须有序
很多人直接用在乱序数组上,结果完全不对

; 把返回值当下标
记住它返回的是迭代器

; 边界没判断

it != v.end()

一定要写

; 自定义排序没传比较函数
这是中高级选手最容易翻车的地方


一句话记忆法

如果你懒得记一堆东西,就记这一句:

lower_bound 找“第一个不小于”,upper_bound 找“第一个大于”


最后

这两个函数本质上就是“封装好的二分查找”,但它们的威力在于:

  • 写法简洁

  • 不容易出错

  • 能直接解决很多区间问题

建议你多用几次,比如:

  • 统计区间

  • 去重处理

  • LIS(最长上升子序列)

用多了,自然就顺手了。

http://www.jsqmd.com/news/519355/

相关文章:

  • 基于FPGA的FOC电流环手动编写Verilog实现:高效、可读性强的源码与Simulink模...
  • 迷宫算法面试通关指南:华为真题详解+DFS/BFS最优解套路
  • SpringBoot实战:5分钟搞定SSE消息推送,告别轮询烦恼
  • 告别人工规则:用MAPPO+自适应环境生成器,手把手教你训练能应对未知障碍物的无人机协同追捕AI
  • 从摄像头到CAN总线:手把手梳理智驾域控制器的数据接口与布线实战
  • 2026年 上海苏州OPC园区租赁招商推荐榜:精选优质产业园区,解析区位优势与服务体系,助力企业高效选址 - 品牌企业推荐师(官方)
  • LangGraph实战:构建具备状态与决策能力的智能体工作流
  • 计算机毕业设计:Python豆瓣图书数据分析平台 Flask框架 可视化 爬虫 书籍 大数据 机器学习(建议收藏)✅
  • 保姆级教程:用trackeval评估dancetrack多目标跟踪结果(附完整文件结构解析)
  • Codeforces Round 2209
  • UI 界面组成,控制界面代码
  • 【面试真题拆解】Java的Static关键字到底怎么用?
  • 3月18日笔记
  • Cookie操作避坑指南:从浏览器复制到Python requests的完整流程解析
  • 保姆级教程:用OpenWRT打造企业级访客WiFi(含防火墙规则+DHCP避坑指南)
  • Xilinx MMCM动态相位调整:从原理到实战的时钟微调指南
  • 信息学奥赛必备:5分钟搞定配对碱基链的两种C++解法(附完整代码)
  • 从PID到深度学习:柔性机器人控制算法演进全解析(附Python示例代码)
  • 从键盘到显示屏:给STM32F4计算器加个OLED界面(I2C驱动教程)
  • 揭示提示工程架构师创新实验室的神秘面纱
  • PyQt5桌面应用内嵌Web地图避坑指南:从QWebEngineView加载到JS交互全流程
  • 华为OceanStor存储管理员密码遗忘?一文详解从串口到Web的完整重置路径
  • Pixel 2XL刷机指南:从AOSP源码编译到烧录的完整流程(附常见错误解决)
  • 基于PLC的煤矿皮带运输机控制系统 plc煤矿皮带运输机采用西门子博途s7-1200编程
  • TPS63000高效DC-DC电源芯片技术规格:调节宽电压范围至最高电压高达效率实现负载断开自...
  • React - React-intl中injectIntl的作用?
  • FineReport报表JS实现动态参数传递与对话框报表交互
  • Supervisor配置文件里environment变量怎么填?一个变量多个路径的实战写法
  • Python自动化界面操作:从基础到实战全攻略
  • 【51单片机实战】波形发生器DIY:从原理图到四种波形输出全解析