std::set 插入重复元素不报错也不覆盖,返回 pair 的 bool 为 false 且 iterator 指向原节点;去重必须检查返回值,不可忽略;初始化列表或迭代器范围构造更高效;自定义类型需提供 operator< 或 Compare 才能使用。

std::set 插入重复元素会怎样
不会报错,也不会覆盖——std::set 本身就不允许重复。第二次 insert() 同一个值,返回的是 std::pair<iterator bool></iterator>,其中 bool 为 false,iterator 指向已存在的那个节点。
常见错误是只调用 insert() 却没检查返回值,误以为“插入成功”就代表新元素进去了;实际可能只是查到了旧值。
- 去重逻辑必须依赖
insert()的返回值,而不是“调用了就完事” - 如果想统计新增了多少个不重复元素,得每次判断
it.second -
std::set的比较基于operator<(或自定义Compare),不是==;两个对象a == b但a < b和b < a都为false时,才会被当作重复
std::set 和 std::unordered_set 去重效果一样吗
去重结果一致,但行为、性能、约束完全不同。
std::set 是红黑树实现,自动按序存储;std::unordered_set 是哈希表,无序。选哪个不只看“要不要排序”,更要看类型是否支持对应操作。
立即学习“C++免费学习笔记(深入)”;
- 要用
std::set:元素类型必须可比较(提供operator<或传入Compare) - 要用
std::unordered_set:元素类型必须可哈希(特化std::hash)且支持== - 插入
int、std::string这类内置/标准类型,两者都行;但自定义结构体默认只能进std::set,除非你手动写哈希和相等判断 - 单次插入平均复杂度:
std::set是O(log n),std::unordered_set是O(1)(均摊),但后者有哈希碰撞和重建桶的隐性开销
std::set 初始化时怎么避免多次 insert
直接用初始化列表,比循环 insert() 更简洁、更高效。
编译器通常会对初始化列表做优化(如直接建树),而循环 insert() 是 n 次独立的 O(log n) 操作,总代价接近 O(n log n)。
std::set<int> s1 = {3, 1, 4, 1, 5}; // 自动去重+排序 → {1,3,4,5}
std::set<int> s2;
s2.insert(3); s2.insert(1); s2.insert(4); s2.insert(1); // 不推荐,冗余
- 初始化列表里重复值会被静默丢弃,不用额外处理
- 如果数据来自容器(比如
std::vector<int>),可以用迭代器范围构造:std::set<int> s(v.begin(), v.end()),也是一次建树 - 注意:迭代器构造方式仍要求元素类型可比较,且不检查输入是否已排序——它不会加速,只是按给定顺序逐个
insert()
std::set 迭代器失效规则和遍历时删元素
删除某个元素,只让指向该元素的迭代器失效;其他迭代器(包括 end())仍然有效。这点和 std::vector 完全不同。
但很多人在 for 循环里边遍历边删,写出类似 it++ 然后 erase(it) 的代码,导致跳过下一个元素或访问已失效迭代器。
- 安全做法是用
erase()的返回值:it = s.erase(it),它返回下一个有效迭代器 - 或者改用 while 循环 + 条件判断,避免在 for 头部写
++it -
clear()会让所有迭代器失效;insert()不影响已有迭代器(这是std::set的关键优势) - 如果只是想删掉满足条件的全部元素,考虑
std::remove_if+erase组合?不行——std::set不支持随机访问,不能用std::remove_if;老老实实迭代删
std::set 做去重+排序时,最常漏掉的是自定义类型的比较逻辑写法,以及误把 insert() 返回值当 void 用。这两处一错,程序看起来“好像能跑”,但去重就悄悄失效了。








