std::equal_range比手写二分更值得用,因为它直接返回[first_not_less, first_greater)迭代器对,精准定位等于目标值的整个区间;一次调用比分别调lower_bound和upper_bound更稳定高效,前提是容器已升序排序且元素支持比较。

std::equal_range 为什么比手写二分更值得用
它直接返回一对迭代器,分别指向第一个不小于目标值、第一个大于目标值的位置——这正是你要的“等于目标值的整个区间”。手写容易漏掉边界条件,比如 std::lower_bound 和 std::upper_bound 各调一次,不如一次调 std::equal_range 稳定又高效。
前提是容器已排序(升序),且元素支持 比较。否则行为未定义,不会报错但结果不可靠。
怎么调用 std::equal_range 才不出错
最常见错误是传入非随机访问迭代器(比如 std::list::iterator),导致编译失败或退化成线性查找。它内部依赖 std::advance 和距离计算,只对 RandomAccessIterator 有对数时间复杂度。
- 必须用
std::vector、std::array或原生数组 +std::begin/std::end - 自定义比较函数时,要和排序时用的完全一致(包括 const 限定、参数顺序)
- 别对
std::map或std::set用它——它们没有重复元素,直接用find更合适
示例:
立即学习“C++免费学习笔记(深入)”;
std::vector<int> v = {1, 2, 2, 2, 3, 4, 4};
auto range = std::equal_range(v.begin(), v.end(), 2); // [v.begin()+1, v.begin()+4)
if (range.first != range.second) {
std::cout << "found " << std::distance(range.first, range.second) << " copies\n";
}equal_range 返回的 pair 到底怎么用
range.first 是左闭边界的起点,range.second 是右开边界的终点。两者相等意味着没找到——不是“找到一个”,而是“一个都没有”。
- 取第一个匹配项:
*(range.first)(需先判断range.first != range.second) - 遍历所有匹配项:
for (auto it = range.first; it != range.second; ++it) - 插入新元素并保持有序:用
range.first作插入位置,避免重复扫描
注意:range.second - range.first 是匹配数量,但仅对随机访问迭代器合法;其他类型得用 std::distance。
和 lower_bound / upper_bound 的性能差异大吗
几乎没差别。标准库实现通常让 std::equal_range 内部复用同一个二分逻辑,只做一次遍历,而不是分别调两次 lower_bound 和 upper_bound。实测在 std::vector 上耗时基本一致。
但语义更清晰:你要的是“范围”,就该用 equal_range;只要左边界,用 lower_bound;只要右边界,用 upper_bound。混用容易造成维护困惑,比如后续有人把排序谓词改了,却只改了一个调用点。
真正容易被忽略的是:如果容器有自定义比较器(比如 std::greater<int>),必须显式传入,否则默认用 <,结果完全错乱。



















