std::set的交集和并集必须用std::set_intersection与std::set_union算法函数,因set未重载operator&或operator|;输入需有序(set迭代器天然满足),输出须另置容器(如vector或新set),且注意去重、性能及线程安全。

std::set 的交集和并集必须用算法函数,不能直接用 operator& 或 operator|
因为 std::set 本身不重载位运算或集合运算符,强行写 a & b 会编译失败。真正可用的是 std::set_intersection 和 std::set_union,它们要求输入是已排序的范围——而 std::set 迭代器天然满足这点,所以能直接用。
常见错误现象:error: no match for 'operator&';或者误以为 insert 多个 set 就是并集(实际是重复插入,效率低且逻辑错)。
- 两个
std::set必须同类型(比如都是std::set<int></int>),否则编译不过 - 输出容器不能是原
set本身(比如不能把交集结果写回a),必须另开一个容器,通常是std::vector或另一个std::set - 输出迭代器要预留空间(对
vector用back_inserter最安全;对set直接用inserter)
std::set<int> a = {1, 2, 3, 4};
std::set<int> b = {3, 4, 5, 6};
std::vector<int> intersect;
std::set_intersection(a.begin(), a.end(),
b.begin(), b.end(),
std::back_inserter(intersect)); // intersect 现在是 {3, 4}
用 set_union 时别漏掉输出容器的去重逻辑
std::set_union 输出的是「合并后的有序序列」,不是自动 dedup 的 std::set。如果你把结果塞进 std::vector,它就是带序、无重复的(因为输入是 set),但如果你手动拼接或混入其他数据,就可能出问题。
使用场景:合并配置项、权限集合、日志事件 ID 集合等需要保序且去重的场合。
立即学习“C++免费学习笔记(深入)”;
- 如果目标是得到一个新
std::set,直接用std::inserter最省心:std::set_union(..., std::inserter(result_set, result_set.end())) - 若用
vector接收,它天然有序无重,但后续若需频繁查找,不如直接建set——毕竟set查找是 O(log n),vector是 O(n) - 注意:
set_union对相同元素只保留一次,哪怕两边都有,它也只写一次——这符合数学并集定义,不是 bug
性能关键点:别在循环里反复调用 set_intersection
每次调用 std::set_intersection 时间复杂度是 O(m + n),看着不高,但如果在高频路径(比如每帧计算、每请求聚合)里对大集合反复算,就成了瓶颈。更糟的是有人用 for (auto x : a) if (b.count(x)) ...,这是 O(m log n),比算法函数慢得多。
- 优先复用已有结果,比如缓存交集结果并在源
set变化时增量更新(适合变化少的场景) - 如果只是判断「是否有交集」,用
std::find_first_of或手写双指针提前退出,比完整求交更快 - 跨线程访问时,确保读操作不和
insert/erase冲突——std::set迭代器不保证线程安全
兼容性注意:C++11 起支持,但 low_bound / upper_bound 行为要留意
所有标准库算法都依赖迭代器的比较行为。std::set 默认用 std::less<key></key>,所以只要 Key 支持 ,就没问题。但自定义比较器(比如忽略大小写的 <code>string)时,交并集结果仍严格按该比较器排序,不是字典序。
- 自定义比较器必须是「严格弱序」,否则
set_intersection可能崩溃或返回乱序结果 - 如果用
std::set<:string std::greater></:string>,交集结果仍是降序,不是升序——算法不改变输入顺序语义 - C++11 之前没
std::set_intersection?不存在的,它从 C++98 就有,但 C++11 加了移动语义优化,对大对象更友好
最常被忽略的是:算法函数不检查输入是否真为 set——你传两个乱序 vector 进去,它照样跑,但结果错得毫无征兆。



















