std::equal_range返回std::pair,first指向首个不小于目标值的位置,second指向首个大于目标值的位置,共同界定所有等于目标值的连续元素区间;要求容器有序且比较逻辑一致。

std::equal_range 返回的是什么,为什么它“一次性”就拿到了区间
它返回的是 std::pair<iterator iterator></iterator>,其中 first 指向第一个不小于目标值的位置,second 指向第一个大于目标值的位置——这俩正好围出所有等于目标值的连续元素范围。前提是容器已排序,且比较逻辑一致。
常见错误是拿到 std::pair 后还手动遍历验证是否相等,纯属重复劳动;更糟的是用 std::find 配合 std::count 模拟,时间复杂度从 O(log n) 退化到 O(n)。
- 必须确保传入的迭代器范围是有序的(升序默认,也可传自定义
Compare) - 若用自定义比较函数(如
std::greater<int></int>),std::equal_range的语义仍保持“等价类区间”,但“等价”由该函数定义 - 对
std::vector、std::array等支持随机访问的容器,底层用二分查找;对std::list不适用——它没有std::equal_range的重载版本
怎么写才不会踩空指针或越界坑
返回的两个迭代器可能都等于 end()(说明没找到),也可能 first == second(找到零个匹配项),也可能 first != second(有匹配)。别直接解引用 first 就以为一定有值。
典型误写:auto it = std::equal_range(v.begin(), v.end(), x); *it.first = 42; —— 如果没找到,it.first 是 v.end(),解引用未定义行为。
立即学习“C++免费学习笔记(深入)”;
- 安全做法:先检查
it.first != it.second再操作区间内元素 - 若只关心是否存在,用
it.first != v.end() && *(it.first) == x更稳妥(因为equal_range在严格弱序下不能保证*it.first == x,尤其用了自定义Compare时) - 对
std::map/std::set,直接用成员函数equal_range(如m.equal_range(key)),比全局版本略快,且类型更安全
和 lower_bound + upper_bound 手动组合比,有啥实际差别
没差别。标准规定 std::equal_range 等价于调用一次 std::lower_bound 和一次 std::upper_bound,但允许实现做优化——比如某些库(如 libstdc++)在随机访问迭代器上只做一次二分过程,把上下界一并算出来。
所以不是“语法糖”,而是潜在的性能友好接口。但别指望在 std::deque 上有质变,它的随机访问代价高,两次二分仍是两次 O(log n)。
- 写
std::lower_bound(a, b, x); std::upper_bound(a, b, x);和写std::equal_range(a, b, x)语义完全等价 - 但前者容易写错:比如两次传不同比较器、或第二次传错迭代器范围
- 如果后续要反复查多个值,且容器不变,考虑预建
std::vector+std::sort+ 一次性批量查询,而不是反复调用equal_range
在 C++20 范围库下怎么更自然地用
C++20 的 std::ranges::equal_range 支持直接传容器、视图或范围适配器,不用再拆 .begin()/.end(),也自动推导比较逻辑。
示例:auto [lo, hi] = std::ranges::equal_range(vec, 42); —— 结构化绑定让意图更清晰,且 vec 可以是 std::vector、std::span,甚至带 filter_view 的懒计算序列(只要满足 random_access_range 和 sorted 概念)。
- 注意:若范围不是
std::ranges::sorted(比如没排过序),行为未定义,编译期不报错,运行时结果不可靠 - 自定义比较器要传给
std::ranges::equal_range第三个参数,不能靠 ADL 或模板实参推导 - 目前主流 STL 实现(libstdc++、libc++)对 ranges 版本的优化程度还不完全等同于传统版,高频调用场景建议压测对比
equal_range 就不会把它们归进同一个区间。










