std::includes判断有序序列的多重集包含关系,要求两范围均按相同规则升序排列,时间复杂度o(n+m);参数顺序为全集在前、子集在后,不处理未排序输入。

std::includes 判断的是「有序序列的子集关系」,不是容器类型意义上的包含
它不关心你用的是 std::vector 还是 std::set,只认两个已排序的范围 —— 如果第二个范围里的每个元素,在第一个范围内都能按序找到(允许重复,但顺序和相对频次不强制),就返回 true。本质是「是否能从第一个序列中按序挑出第二个序列的所有元素」,类似“子序列匹配”,但要求严格单调不降(因为输入必须升序)。
常见误用:拿两个未排序的 std::vector 直接传给 std::includes,结果不可预测 —— 它不会帮你排序,也不会报错,只是逻辑失效。
- 必须确保两个输入范围都已用相同规则升序排列(比如都用
std::less<int></int>) - 若用自定义比较器(如
std::greater<int></int>),两个范围都得按该规则排好序 -
std::set和std::map::key_set()天然满足条件,可直接用其迭代器;std::vector需先调用std::sort
std::includes 的参数顺序不能颠倒:subset 在后,superset 在前
函数签名是 std::includes(first1, last1, first2, last2),其中 [first1, last1) 是被检查的「全集」,[first2, last2) 是待验证的「子集」。顺序反了会直接逻辑翻转 —— 比如你想查 A 是否包含 B,却写成 includes(B.begin(), B.end(), A.begin(), A.end()),那实际是在查 B 是否包含 A。
容易踩坑的点:
立即学习“C++免费学习笔记(深入)”;
- 变量命名模糊(如都叫
vec1,vec2)时极易传反 - 配合
auto推导迭代器时,没注意哪个容器更大,靠直觉传参 - 在模板函数里泛化调用,忘记约束参数语义,导致运行时结果与预期相反
重复元素处理:std::includes 要求「频次覆盖」,不是集合去重意义上的包含
它不是数学集合论中的 ⊆,而是多重集(multiset)意义上的包含。例如:
std::vector<int> sup = {1, 2, 2, 3};
std::vector<int> sub = {2, 2};
// std::includes(sup.begin(), sup.end(), sub.begin(), sub.end()) → true
std::vector<int> sub2 = {2, 2, 2};
// → false,因为 sup 中只有两个 2
也就是说,std::includes 内部做的是双指针遍历:对 sub 中每个元素,在 sup 中找「下一个 ≥ 它」的位置,并消耗一个匹配项。所以:
- 如果
sub有重复值,sup中对应值的出现次数必须不少于sub中的次数 - 若想实现纯集合语义(忽略重复),需先用
std::unique+std::erase去重,或改用std::set存储 - 性能上,它是 O(n+m) 时间、O(1) 空间,比构造哈希表判断快,但前提是已排序
和 std::set_intersection、std::find_first_of 的关键区别在哪
std::includes 不产生新数据,只返回布尔结果;而 std::set_intersection 会把交集写入目标迭代器 —— 如果你只需要判断关系,用 includes 更轻量;如果后续还要用交集内容,别重复遍历两次。
std::find_first_of 是「是否存在任一匹配」,属于存在性判断;std::includes 是「全部元素都匹配」,属于全称判断。两者语义层级不同,不能互相替代。
- 要查「B 中所有元素都在 A 里」→ 用
std::includes(A.begin(), A.end(), B.begin(), B.end()) - 要查「B 中至少有一个元素在 A 里」→ 用
std::find_first_of(A.begin(), A.end(), B.begin(), B.end()) != A.end() - 要获取 A ∩ B 的具体元素 → 用
std::set_intersection,并确保输出空间足够
最易被忽略的一点:所有这些算法都依赖「已排序」前提。哪怕只漏掉一次 std::sort,或者比较器不一致(比如一个用默认 ,另一个用了 <code>std::greater),结果就完全不可信 —— 而且通常不崩溃,只是静默错误。










