std::set_intersection等算法要求输入有序且不重叠,std::set天然满足,但混用vector等需手动排序;输出应避免重叠内存、优先用vector+back_inserter,注意区间左闭右开及输出容器清空。

用 std::set_intersection 等算法前,必须保证两个 std::set 有序且不重复
这是最容易被忽略的前提:std::set 本身已自动排序(升序)且去重,所以你不用手动排序,但如果你混用 std::vector 或其他容器做输入,就可能出错。这些算法要求输入迭代器范围是“已排序的”,否则行为未定义——不是报错,而是结果随机。
实操建议:
- 交集、并集、差集都需输出到另一个容器,且该容器需预留空间或用
std::back_inserter;直接写入空std::set会因插入顺序导致性能下降(std::set插入是O(log n),而算法本身是O(m+n)) - 输出容器类型不限于
std::set,常用std::vector更高效,尤其当后续还要遍历或传给其他函数时 - 所有算法头文件统一为
<algorithm></algorithm>,别漏掉
std::set_intersection 的输出容器不能是输入之一
比如你写 set_intersection(a.begin(), a.end(), b.begin(), b.end(), a.begin()) 是错的——这属于“重叠内存操作”,标准库不保证安全,多数实现会崩溃或产生垃圾值。
正确做法:
- 用
std::vector<int> result</int>接收,再用std::back_inserter(result) - 如果硬要输出到
std::set,先清空目标集,再逐个insert(但不如用 vector + 构造新 set 快) - 注意:输入区间必须是左闭右开,即
a.end()正确,++a.end()就越界了
示例:
std::set<int> a = {1,2,3,4}, b = {3,4,5,6};
std::vector<int> inter;
std::set_intersection(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(inter));
// inter == {3,4}</int></int>
std::set_difference 不是 “A 减 B” 就一定比 “B 减 A” 元素少
它严格按第一个区间减第二个区间:即保留那些在第一区间出现、但不在第二区间出现的元素。顺序很重要,而且和数学集合差不同——它不自动去重,只是按有序序列做线性扫描匹配。
常见误解:
- 认为
set_difference(a,b)和set_difference(b,a)是互补关系(实际它们并集不等于全集,因为算法只扫一次,不关心全局补集) - 把
std::set_difference当作“对称差”用(那是std::set_symmetric_difference的事) - 忘记输出容器初始为空——若复用非空 vector,结果会追加在末尾,造成脏数据
性能关键:别在循环里反复调用这些算法处理大集合
每个算法时间复杂度是 O(m + n),看着不错,但如果在高频逻辑中(比如每帧调用),而 std::set 本身节点分配开销大,叠加迭代器解引用成本,实际比手写哈希表查一遍慢不少。
替代思路:
- 若频繁求交集,考虑把小集合转成
std::unordered_set,然后遍历大集合做count()判断,常数级更快 - 若集合内容不变,提前算好结果缓存起来,避免重复计算
-
std::set迭代器是双向的,不是随机访问,所以算法内部只能线性推进,没法跳着走——这点和std::vector上的二分搜索不同
真正卡点的地方往往不是算法选错,而是没意识到 std::set 的内存布局和迭代开销已经成了瓶颈。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











