std::equal_range 是标准算法,用于在已排序 vector 中查找目标值的所有匹配项,返回 pair 迭代器,[first, second) 包含所有等于目标的元素,使用前须确保容器升序排列且比较逻辑一致。

equal_range 在 sorted vector 中怎么用
equal_range 不是容器成员函数,而是标准算法,必须配合 std::lower_bound 和 std::upper_bound 的语义一起理解:它返回一个 std::pair<it it></it>,其中 first 指向第一个不小于目标值的元素,second 指向第一个大于目标值的元素——也就是说,区间 [first, second) 正好包含所有等于目标值的元素。
它要求输入迭代器范围已按升序排序(默认比较),否则行为未定义:
std::vector<int> v = {1, 2, 2, 2, 3, 4, 4};
auto range = std::equal_range(v.begin(), v.end(), 2);
// range.first → 指向第1个2(索引1)
// range.second → 指向第1个3(索引4)
// 所以 range.second - range.first == 3
- 必须传入左闭右开区间,比如
v.begin()和v.end(),不能传v.data()+ size 混用 - 如果目标不存在,
range.first == range.second,此时解引用任一迭代器都是越界行为 - 对
std::vector、std::array等支持随机访问的容器,复杂度是O(log n);对std::list这类仅支持双向迭代器的容器,equal_range退化为O(n),不推荐使用
在 set/multiset 中别用 equal_range
std::set 和 std::multiset 是关联容器,它们自己提供了成员版的 equal_range,和算法版同名但行为更安全、更高效:
std::multiset<int> s = {2, 2, 2, 3, 4};
auto range = s.equal_range(2); // ✅ 成员函数,直接调用
- 成员版能利用红黑树内部结构,无需额外比较器参数,也不依赖外部排序状态
- 算法版
std::equal_range(s.begin(), s.end(), ...)虽然语法合法,但绕过容器优化,还可能因迭代器 category 导致性能下降 - 对
std::set(键唯一),equal_range返回的区间长度只能是 0 或 1;对std::multiset才真正有意义
自定义类型要用 const& 传参,且比较逻辑必须一致
如果你用 equal_range 查找自定义结构体,最容易出错的是:谓词(Compare)和构造对象时的“相等”逻辑不统一。例如:
立即学习“C++免费学习笔记(深入)”;
struct Person {
std::string name;
int age;
};
bool operator<(const Person& a, const Person& b) {
return a.name < b.name; // 只按 name 排序
}
std::vector<Person> v = {{"Alice", 30}, {"Bob", 25}, {"Bob", 35}};
auto range = std::equal_range(v.begin(), v.end(), Person{"Bob", 0});
// ❌ 错!Person{"Bob", 0} 构造临时对象,但比较只看 name,没问题;
// 但若你写了自定义 Compare,又忘了在 equal_range 中传同一个,就会错
- 务必确保排序用的比较逻辑(构造容器时或
sort时用的谓词)和equal_range的第三个参数(或第四个)完全一致 - 传入的目标值尽量用
const T&避免无谓拷贝,尤其对大对象 - 如果用了自定义
Compare,必须显式传给equal_range,否则默认用,容易和实际排序方式冲突
查不到时怎么安全取值
equal_range 不抛异常,查不到只是返回两个相等的迭代器。常见误操作是直接 *range.first 解引用:
auto range = std::equal_range(v.begin(), v.end(), 999);
if (range.first != range.second) {
std::cout << *range.first << "\n"; // ✅ 安全
} else {
std::cout << "not found\n";
}
- 永远先判断
range.first != range.second再解引用,哪怕你知道“应该有” - 想取第一个匹配项?用
range.first;想取最后一个?用std::prev(range.second)(需确保非空) - 想遍历所有匹配项?用
for (auto it = range.first; it != range.second; ++it),别手写 while
最常被忽略的是:算法版 equal_range 对 std::vector<t></t> 这类连续内存容器,底层其实调用了 lower_bound + upper_bound 两次二分查找——它不会缓存中间结果。如果既要查区间又要查单个位置,不如手动调一次 lower_bound,再从那里线性扫到不同值为止,反而更快(尤其重复值少时)。










