<p>左闭右开区间更不易出边界错误,因为right始终表示“搜索范围不包含的首个位置”,循环条件统一用left < right,边界更新逻辑一致(right = mid),避免了闭区间中right = mid - 1易导致的off-by-one错误。</p>

为什么左闭右开区间比左闭右闭更不容易出边界错误
因为 right 始终表示“搜索范围不包含的首个位置”,循环条件统一用 left ,无需纠结 <code>mid 加减 1 后是否越界。所有边界操作(比如 right = mid)都天然安全,不用像左闭右闭那样在 right = mid - 1 时担心下溢。
模板函数怎么写才能支持任意随机访问迭代器和自定义比较
必须用 std::iterator_traits 提取迭代器类型,并让比较函数可选,默认用 。关键点是:迭代器要满足 <code>RandomAccessIterator 要求,否则 mid = first + (last - first) / 2 编译不过。
template<typename It, typename T, typename Compare = std::less<typename std::iterator_traits<It>::value_type>>
It lower_bound(It first, It last, const T& val, Compare comp = Compare{}) {
while (first < last) {
It mid = first + (last - first) / 2;
if (comp(*mid, val)) {
first = mid + 1;
} else {
last = mid;
}
}
return first;
}调用时传数组、vector 或 raw 指针要注意什么
左闭右开区间的 last 必须是“末尾后一个位置”——这是最容易错的地方。常见错误包括:
- 对
std::vector<int> v,传v.begin(), v.end()正确;传v.data(), v.data() + v.size()也正确;但传v.data(), v.data() + v.size() - 1就错了 - 对 C 风格数组
int arr[5],必须传arr, arr + 5,不能传arr, &arr[4] - 如果容器为空,
first == last,循环不进入,直接返回first,逻辑依然成立
和 std::lower_bound 的行为差异在哪
你写的模板如果完全按上述实现,行为就和 std::lower_bound 一致——返回第一个不小于 val 的位置。但注意两点:
立即学习“C++免费学习笔记(深入)”;
-
std::lower_bound要求范围已排序,你的模板也一样;没排序结果未定义 - 如果你用自定义
Compare,比如std::greater<int>,那搜索的是“第一个不大于val的位置”,此时数据必须是降序排列,否则结果不可靠 - 性能上无差异,都是 O(log n),但模板实例化可能略微增加编译时间
真正容易被忽略的是:当目标不存在时,返回的迭代器可能等于 last,也就是越界位置——这时候解引用会崩溃,用之前务必检查是否 != last。


















