std::set自动去重并按升序排序,底层基于红黑树实现;插入重复元素被忽略,返回值.second指示是否成功插入;降序需传std::greater{};自定义类型须提供operator

set 本身就会自动去重和排序,不需要额外操作
你往 std::set 里插重复元素,它直接无视——不是报错,也不是覆盖,是“不存”。底层用红黑树实现,插入时就按 operator(或自定义比较器)排序,所以遍历出来天然升序、无重复。
常见错误现象:set.insert(x) 返回一个 std::pair<iterator bool></iterator>,很多人只取迭代器,却忽略 second 字段才是“是否真插入了”的判断依据。误以为插完就一定有新元素,结果逻辑出错。
- 如果需要知道某次插入是否成功,必须检查返回值的
.second - 想按降序排列?传
std::greater<int>{}</int>作模板参数:std::set<int std::greater>></int> - 自定义类型必须提供可比较的
operator,或显式传比较函数对象;否则编译失败,错误信息类似:<code>invalid operands to binary expression ('const MyType' and 'const MyType')
别用 set 去处理已有重复数据再“去重”
有人把一堆重复数字先塞进 vector,再一个个 insert 到 set 里,以为这是“去重手段”。这可行但低效:每次 insert 是 O(log n),总时间 O(m log n)(m 是原始元素数,n 是去重后大小),远不如先 sort + unique —— 后者是 O(m log m) 排序 + O(m) 扫描,对大数组更友好。
- 场景明确是“一次性清洗已有容器”,优先考虑
std::sort+std::unique+erase -
set的优势在动态增删+实时有序,比如维护一个不断变化的排行榜、查找中位数、区间查询等 - 内存开销更大:每个节点要存左右子节点指针+颜色位,比
vector或unordered_set占更多空间
想保留插入顺序又去重?set 不行,换 unordered_set 或 vector + find
std::set 天然破坏插入顺序——它只认大小关系。如果你需要“第一次出现的元素排前面,后续重复跳过”,set 无法满足。
立即学习“C++免费学习笔记(深入)”;
- 简单场景(小数据、不频繁查):用
std::vector,插入前用std::find检查是否存在 -
大数据量且查得多:用
std::unordered_set辅助判重,同时往vector插,O(1)平均判重 + 保持顺序 - 别试图给
set加时间戳字段再重载operator 来模拟顺序——这会让排序逻辑混乱,且失去“按值比较”的语义,后续 <code>find可能失效
迭代器失效规则比 vector 简单,但也得小心
set 迭代器只有在对应元素被 erase 时才失效,插入不影响其他迭代器。这点比 vector 安全,但仍有坑:
-
it = s.erase(it)是安全的,返回下一个有效迭代器;但写成s.erase(it); ++it就是野指针 - 用范围 for 循环遍历时删除当前元素?不行:
for (auto x : s) { if (x == target) s.erase(x); }—— 这会触发未定义行为,必须改用传统 for + erase 返回值 - 多线程下,任何非 const 操作都需外部同步;
const操作如find、count可并发,但不能和修改操作同时发生
红黑树的细节不用深究,但得记住:它保证了对数级操作、严格弱序、自动去重。用错场景比用错语法更难调试——比如拿它当缓存容器,却没意识到 erase 最坏是 O(n)(实际是 O(log n),但常数大),或者误以为 begin() 是最小元素就等于“最早插入”,其实只是最小值。










