std::set_intersection要求两输入范围已排序,是对有序迭代器范围的归并扫描;若容器无序(如unordered_set)或未排序,将返回空结果。

std::set_intersection 要求两个输入范围必须已排序
它不是“对任意两个 std::set 求交”,而是对两个**已排序的迭代器范围**做归并扫描。哪怕你传的是 std::set<int>::begin()</int> 和 std::set<int>::end()</int>,也只因 std::set 天然有序才“碰巧能用”——换成 std::vector 就得先 std::sort。
常见错误现象:std::set_intersection 返回空结果,但手动检查发现明明有公共元素——大概率是其中一个容器没排序,或用了 std::unordered_set 这类无序容器直接传迭代器。
- 使用场景:两个升序序列(如
std::set、std::vector排好序后)求交集,且你希望复用已有内存(比如写入已有std::vector的预留空间) - 参数差异:第 5 个参数是输出迭代器,必须支持写入(如
std::back_inserter或普通指针),不能是只读范围 - 性能影响:时间复杂度 O(m + n),比遍历+查找快;但要求输入有序,否则预处理排序成本可能抵消优势
别直接往 std::set 里 insert set_intersection 的结果
std::set_intersection 不会自动去重或保持红黑树结构,它只是把交集元素按序“抄”出来。如果你目标是得到一个 std::set,最安全做法是先写入临时容器,再构造新 std::set。
错误写法示例:std::set_intersection(a.begin(), a.end(), b.begin(), b.end(), my_set.begin()) —— my_set.begin() 是 const 迭代器,且 std::set 不支持随机写入;就算用 my_set.insert(...) 手动插,也得自己控制迭代器位置,极易越界或漏插。
立即学习“C++免费学习笔记(深入)”;
- 正确做法:用
std::vector接收结果,再用该 vector 初始化新std::set,或用std::inserter(my_set, my_set.end()) - 注意
std::inserter内部调用insert,对每个元素做 log(n) 查找,整体变成 O(k·log n),k 是交集大小;而先写 vector 再建 set 是 O(k + k·log k),通常更快 - 如果原数据就是
std::set,且只需交集逻辑,其实直接遍历小的那个 set、在大的里面find更直观,代码更少、意图更清
std::set_intersection 对自定义类型必须提供严格弱序比较
如果你用的是 std::set<mystruct></mystruct>,那 std::set_intersection 传入的两个范围,必须用**同一个比较函数对象**,且该函数必须满足严格弱序(strict weak ordering)——比如不能只写 a.x ,必须是 <code>a.x 。
常见错误现象:程序崩溃、结果重复、漏元素,甚至 debug 版本断言失败。根本原因是比较逻辑不一致,导致归并过程误判“相等”或“小于”关系。
- 务必确认:传给
std::set构造的比较器,和传给std::set_intersection的第 6 个参数(可选)完全一致 - 如果两个输入 range 来自不同容器(比如一个
std::set,一个排好序的std::vector),它们的排序依据必须严格相同,否则结果不可靠 - 用 lambda 时注意生命周期:若捕获局部变量,确保其在
set_intersection执行期间仍有效
替代方案:用 set 的成员函数 find 更简单?
当一边是 std::set、另一边是小规模容器(比如 size find 往往比 std::set_intersection 更省事、更不容易出错。
例如:for (const auto& x : small_vec) { if (big_set.find(x) != big_set.end()) result.push_back(x); } —— 逻辑直白,不用管排序、不用配迭代器、不依赖额外头文件(<algorithm> 已隐含)。
- 性能上:O(n·log m),n 是小容器大小,m 是 set 大小;而
set_intersection是 O(n + m),但前提是两边都已排序且你愿意写一堆模板参数 - 兼容性更好:不依赖输入是否“恰好有序”,也不怕自定义类型比较器漏传
- 真正容易被忽略的点:
std::set_intersection的输出迭代器必须有足够的空间(或用back_inserter),而手写循环天然适配任何输出方式(push、emplace、甚至直接处理)











