std::includes要求两序列均升序(或同自定义序),用双指针线性扫描判断子集关系,时间复杂度O(n+m),不排序、不处理无序容器,set/multiset可直接用,unordered_set不可用。

std::includes 判断两个有序范围是否构成子集关系
它只认“升序排列的序列”,不是任意两个 std::set 或随便一塞的 vector 就能直接用。底层逻辑是双指针归并扫描,要求双方都已排序,否则行为未定义——常见错误是传入未排序容器后结果随机或崩溃。
- 必须确保
first1到last1和first2到last2都是升序(默认比较);若用自定义排序,两个范围必须用同一Compare - 它判断的是“
[first2, last2)中每个元素是否都在[first1, last1)中出现过”,不关心重复次数:比如{1,1,2}包含{1,2},但不包含{1,1,1} - 时间复杂度 O(n + m),比逐个调用
std::binary_search快,也比建std::set后查快,尤其当数据已在内存中排好序时
用 vector 调用 std::includes 前必须先 sort
很多人以为 vector 天然支持 std::includes,结果传进去一堆乱序数,返回 false 却查不出原因。它不负责排序,只做线性验证。
- 正确流程:
std::sort(v1.begin(), v1.end()); std::sort(v2.begin(), v2.end()); bool res = std::includes(v1.begin(), v1.end(), v2.begin(), v2.end()); - 如果 v2 是 v1 的子集但 v1 未排序,
std::includes可能提前终止、漏判,甚至因越界访问触发 ASan 报错__builtin_memmove_chk: detected overflow - 若数据来自外部(如文件读入、网络接收),别假设“看起来有序”——浮点误差、负数、字符串字典序等都可能打破严格升序
std::includes 对 set/multiset 容器可以直接用
std::set 和 std::multiset 内部有序,迭代器天然满足升序要求,所以可直接传 begin()/end(),无需额外排序。
- 示例:
std::set<int> a = {1,2,3,4,5}, b = {2,3}; bool ok = std::includes(a.begin(), a.end(), b.begin(), b.end()); // true</int> - 注意
std::multiset允许重复,std::includes会检查频次:若 b 有三个 2,a 至少要有三个 2 才算包含 - 但
std::unordered_set完全不能用——无序容器迭代器不满足前提,编译可能过,运行必错
自定义比较函数时两个范围必须一致
用 std::greater<int>()</int> 排降序?那两个范围都得按降序排好,并且 std::includes 调用时显式传同一个 Compare 对象。
立即学习“C++免费学习笔记(深入)”;
- 错误写法:
std::sort(v1.begin(), v1.end(), std::greater<int>()); std::sort(v2.begin(), v2.end()); std::includes(v1.begin(), v1.end(), v2.begin(), v2.end(), std::greater<int>());</int></int>→ v2 是升序,却用降序比较,结果不可信 - 正确写法:v2 也要用
std::greater<int>()</int>排序,且Compare类型和值必须完全相同(包括 lambda 捕获状态) - 结构体场景下,若重载了
operator<,但又用std::less<T>()显式传参,其实等价;但混用std::greater<T>()就容易翻车
最容易被忽略的一点:它不检查容器类型,只看迭代器行为。传 std::list 也可以,但性能差(双向迭代器导致内部做多次 ++/--);而 std::vector 的随机访问优势在 std::includes 里根本用不上——它只按顺序走,不跳转。











