直接用 count() 判断存在性,返回 1 或 0;insert() 返回 pair 的 second 表示是否成功;遍历时删除需用 erase 返回值更新迭代器;有序选 set,快速查存选 unordered_set。

怎么判断一个值是否在 std::set 里
查是否存在是 std::set 最常用操作,别用 find() 后再比较迭代器——虽然能用,但语义不清还多写两行。直接用 count() 更直白,返回 1 或 0,和你想表达的“有没有”完全对应。
注意:count() 对 std::set 永远只返回 0 或 1(没有重复),但它底层仍是 O(log n),不是哈希表那种均摊 O(1)。别误以为它快过 unordered_set。
常见错误:把 count() 当成返回元素个数的通用接口,结果在 multiset 里才真有多个;但在 set 里这么用没问题,只是别混淆语义。
示例:
<pre class="brush:php;toolbar:false;">std::set<int> s = {1, 3, 5, 7};<br>if (s.count(5)) {<br> // 存在<br>}
立即学习“C++免费学习笔记(深入)”;
插入失败时怎么知道是重复还是内存不足
std::set::insert() 返回一个 <code>std::pair<iterator bool></iterator>,其中 second 才是你该看的标志位:为 true 表示插入成功,false 表示键已存在。它永远不会因为“内存不足”失败——std::set 插入失败只有一种原因:键重复。
真正可能抛异常的是内存分配环节,比如构造时传入自定义分配器且分配失败,但这种情况极少见,标准库通常会抛 std::bad_alloc,不是 insert() 的返回值能反映的。
实操建议:
- 永远检查返回值的
.second字段,而不是靠捕获异常来判断重复 - 别在循环里反复
insert()然后忽略返回值——既浪费O(log n),又掩盖逻辑问题 - 如果要批量去重插入,考虑先用
std::vector收集,再用set构造函数初始化,效率更高
遍历 std::set 时能不能边遍历边删元素
可以,但必须用返回值接管下一个有效迭代器,不能写 it++ 后再删 it。因为 erase(it) 会让 it 失效,而 it++ 是先取值再递增,此时取到的已是悬垂指针。
正确做法是用 erase() 的返回值——它返回被删元素之后的合法迭代器。C++11 起 std::set::erase(iterator) 就支持这个用法。
示例:
<pre class="brush:php;toolbar:false;">for (auto it = s.begin(); it != s.end(); ) {<br> if (*it % 2 == 0) {<br> it = s.erase(it); // 安全:it 指向下一个<br> } else {<br> ++it;<br> }<br>}
容易踩的坑:
- 写成
s.erase(it++); —— <code>it仍会被递增,但 erase 后it已无效,行为未定义 - 在范围
for循环里调erase()—— 迭代器自动失效,直接崩溃 - 误以为
erase(key)版本也能返回迭代器 —— 它返回删掉的元素个数(size_t),没法用于遍历控制
std::set 和 std::unordered_set 到底该选哪个
看场景:需要有序遍历、范围查询(比如找所有大于 100 的数)、或频繁做集合运算(set_intersection)就选 std::set;只要快速查存删,且不关心顺序,选 std::unordered_set。
性能差异明显:std::set 所有操作都是 O(log n),基于红黑树;std::unordered_set 平均 O(1),但最坏退化到 O(n)(哈希冲突严重时),而且不保证迭代顺序。
兼容性注意点:
-
std::set要求 key 可比较(默认operator),<code>unordered_set要求可哈希(需特化std::hash) - 自定义类型用
unordered_set时,漏写哈希函数或等价判断,编译直接报错,错误信息往往指向内部模板深处,很难定位 - 如果数据量小(
复杂点在于:有些需求表面要“有序”,其实只是调试时想看整齐输出——这时用 unordered_set + 临时转 vector 排序更轻量;而有些看似只要查存删,但后续要按插入顺序遍历,那就得换容器了。










