应改写比较逻辑:std::lower_bound 默认找第一个不小于目标值的位置,而需找第一个小于目标值的位置,本质是上界减一,但因越界风险不能简单退位,须自定义比较函数实现二分查找。

std::lower_bound 不能直接用,得改写比较逻辑
标准库的 std::lower_bound 找的是第一个不小于目标值的位置,而你需要的是第一个小于目标值的位置——这本质是“上界减一”,但不能简单用 std::upper_bound 后退,因为目标可能不存在、迭代器可能在 begin 或 end,退一步会越界。
正确做法是复用二分结构,把比较函数从 a 改为 <code>a 的反向语义:我们实际在找满足 <code>arr[i] 的最大索引 <code>i,等价于在条件成立的右边界上做二分。
- 使用左闭右开区间
[left, right),初始化left = begin,right = end - 循环中计算
mid = left + (right - left) / 2,避免溢出 - 若
*mid ,说明该位置合法,答案至少在 <code>[mid, right),所以left = mid(注意不是mid + 1) - 否则
right = mid - 循环结束时
left == right,返回left - 1(即最后一个满足的位置),但要先判断是否有效:若 <code>left == begin,说明没有元素小于target,返回end或标记无效
手写模板函数时注意迭代器类型和边界安全
泛型实现必须支持随机访问迭代器(std::vector、std::array、裸指针),且不能假设 end - 1 可解引用——比如空容器或所有元素都 ≥ target 时,left 可能等于 begin,此时 left - 1 是非法地址。
推荐返回一个可判空的 std::optional<it></it>,或约定返回 end 表示未找到:
template <typename it typename t>
It find_first_less(It begin, It end, const T& target) {
if (begin == end) return end;
It left = begin, right = end;
while (left <p>关键点:上面这个版本更稳健——它实际找的是第一个不满足 <code>*it 的位置,然后回退。比“直接扩张左边界”更符合常规二分习惯,也更容易验证边界。</code></p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2823" title="C++14"><img
src="https://img.php.cn/upload/manual/001/431/639/6ac8b33c327c4749.png" alt="C++14" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="overflowclass">C++14</a>
<p class="overflowclass">C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。</p>
</div>
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<h3>用 std::partition_point 做更简洁的表达(C++11起)</h3>
<p>如果你的视图已按升序排列,且想避免手写循环,<code>std::partition_point</code> 是最贴切的工具:它要求序列被某谓词划分为 [true, true, ..., false, false] 形式,返回第一个 false 的位置。你只需传入 <code>[&target](const auto& x) { return x ,它返回的就是第一个不满足 <code>x 的位置 —— 那么前一个就是你要的“最后一个满足 <code>”的位置。</code></code></code></p>
<ul>
<li>代码短:<code>auto it = std::partition_point(begin, end, [&target](const auto& x) { return x </code>
</li>
<li>语义清晰,且标准库保证 O(log N),底层也是二分</li>
<li>同样要注意:若 <code>it == begin</code>,说明没找到;否则 <code>it - 1</code> 是答案</li>
<li>不适用于非随机访问迭代器(如 list),因为需要 O(1) 距离计算</li>
</ul>
<h3>容易忽略的细节:严格小于 vs 小于等于、空范围与 NaN</h3>
<p>如果目标值是浮点数且含 <code>NaN</code>,<code>operator 对 <code>NaN</code> 返回 false,会导致整个二分行为异常(所有比较失败,<code>right</code> 不收缩)。务必提前检查 <code>!std::isnan(target)</code>。</code></p>
<p>另一个坑是误把“小于”理解成“小于等于”:比如数组 <code>[1,2,3,4,5]</code>,找小于 <code>3</code> 的第一个位置,结果应是索引 1(值为 2),不是索引 0(值为 1)——“第一个小于”指位置最靠左的满足值,不是值最小的那个。但你的需求明确是“第一个小于”,所以是找最左的满足点,不是最右的。</p>
<p>最后提醒:所有方案都依赖容器已排序。如果视图是降序,要么反转谓词(用 <code>target ),要么先 reverse_iterator 包装,别硬调参数。</code></p></typename>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










