PAYATHON 2026

C++ lower_bound 为什么找不到区间中已有的元素?

数据小许

第二段代码没有满足 std::lower_bound 对查找区间的分区要求,因此无法保证得到有效结果。这不能说明标准库存在 bug。

lower_bound 对输入有什么要求?

lower_bound 使用二分查找,不会逐个检查元素。对于这里的默认比较,查找区间必须按 element < 4 的结果分区:小于 4 的元素都在前面,不小于 4 的元素都在后面。

满足这个条件时,它返回第一个不小于 4 的元素;如果没有这样的元素,就返回传入区间的终点。

升序排列的区间满足这一要求。严格来说,算法只要求区间针对本次查找值正确分区,不要求整个容器都有序。

为什么第一段成功,第二段失败?

两次调用都从 udata.begin() + 1 开始,因此首元素 5 不参与查找。

第一段实际查找的区间只有:

[4]

它满足分区要求,lower_bound 会返回指向 4 的迭代器。

第二段实际查找的区间是:

[4, 1, 2]

各元素与目标值的比较结果为:

元素:        4      1     2
元素 < 4:  false  true  true

不小于 4 的元素出现在小于 4 的元素之前,违反了分区要求。此时调用的行为未定义,不能依赖它返回什么,也不能保证断言通过。

常见的二分实现可能先检查中间元素 1,因为 1 < 4 而排除左半部分,连同其中的 4 一起跳过,最终返回 end()。这可以解释观察到的现象,但不能保证该输入下一定会得到这个结果。

需要二分查找时,先排序查找区间

如果允许调整元素顺序,可以先对实际查找区间排序:

#include <algorithm>
#include <cassert>
#include <cstddef>
#include <vector>

int main() {
    std::vector<std::size_t> udata = {5, 4, 1, 2};

    std::sort(udata.begin() + 1, udata.end());
    // 整个容器变为 {5, 1, 2, 4}。
    // 实际查找区间为 {1, 2, 4}。

    auto it = std::lower_bound(udata.begin() + 1, udata.end(), 4);
    assert(it != udata.end());
    assert(*it == 4);
}

如果要确认目标值确实存在,需要先判断迭代器不是 end(),再检查 *it == 4。lower_bound 也可能返回大于目标值的元素。

需要保留原顺序时,使用 find_if

如果要在未排序区间中按原顺序查找第一个大于或等于 4 的元素,可以使用线性查找:

std::vector<std::size_t> udata = {5, 4, 1, 2};

auto it = std::find_if(
    udata.begin() + 1,
    udata.end(),
    [](std::size_t value) { return value >= 4; }
);

assert(it != udata.end());
assert(*it == 4);

find_if 不要求区间有序,最坏情况下需要检查整个区间。如果只想查找恰好等于 4 的元素,可以使用 std::find。

备注:内容仅供参考。