std::set通过红黑树实现自动去重与升序排序,插入重复元素时直接忽略并返回pair中second为false;需元素可比较(如重载operator

set 插入自动去重的原理和基本用法
std::set 本身是有序、不重复的关联容器,只要把元素往里 insert() 或用初始化列表构造,重复值就直接被忽略——不是“事后去重”,而是“根本插不进去”。这比先存 vector 再 sort + unique 省事也更安全。
常见错误是误以为 set 能对已有容器“批量去重”,其实它不提供类似 remove_duplicates() 的接口;你要的是“用 set 重建唯一集合”这个动作。
-
set要求元素可比较(默认用operator),自定义类型需提供比较逻辑,否则编译失败 - 插入单个元素用
my_set.insert(x),返回std::pair<iterator bool></iterator>,其中second是是否插入成功(true表示新元素) - 从 vector 构造:
std::set<int> s(vec.begin(), vec.end());</int>—— 这是最常用的一行去重写法
vector 去重后转回 vector 的典型写法
很多场景下你最终还是要一个 vector(比如要随机访问或传给 C 接口),不能只留着 set。这时候别手写循环判断,直接构造再拷贝:
std::vector<int> vec = {1, 2, 2, 3, 3, 4};
std::set<int> s(vec.begin(), vec.end());
std::vector<int> unique_vec(s.begin(), s.end()); // 自动升序
</int></int></int>
注意:结果是升序排列的。如果必须保持原始顺序,set 就不适合——得换 unordered_set 配合遍历标记,或者用 std::unique + sort 组合。
- 保持原序且去重 → 改用
std::unordered_set记录已见元素,边遍历边 push 到新 vector - 数据量大(如百万级)且不关心顺序 →
set构造开销是O(n log n),比unordered_set的平均O(n)慢,但更稳定(无哈希碰撞风险) - 若 vector 已排序,用
std::unique更快:auto it = std::unique(vec.begin(), vec.end()); vec.erase(it, vec.end());
处理自定义结构体时怎么让 set 正确去重
struct 默认不可比较,直接塞进 set<mystruct></mystruct> 会编译报错:invalid operands to binary expression ('const MyStruct' and 'const MyStruct')。必须显式定义“什么叫相等/小于”。
最稳妥的方式是重载 operator(注意不是 <code>==):
struct Point {
int x, y;
bool operator pts = {{1,2}, {1,2}, {3,4}}; // 只保留两个
- 不要在
operator 里用 <code>==判断相等,set依赖严格弱序,只靠就能区分重复 - 如果不想改结构体定义,可用自定义比较器:
std::set<point decltype> s(cmp);</point>,其中cmp是 lambda 或函数对象 - 用
unordered_set则需同时提供哈希函数和==,复杂度更高,一般优先走set+operator
性能与陷阱:什么时候不该用 set 去重
看似简单,但几个边界情况容易翻车:
- 元素类型含指针或浮点数?
set用比较浮点可能因精度导致本该相等的值被当成不同;指针比较的是地址而非所指内容 - 频繁插入少量数据?
set每次插入都涉及内存分配和树调整,小规模(比如 vector +std::find可能更快 - 需要线程安全?
set本身不带同步,多线程读写必须自己加锁,别想当然认为“只读 safe”——迭代器失效规则在并发下极难控制 - 内存受限环境?
set每个节点至少带两个指针(红黑树),比vector占用高一倍以上
真正关键的不是“能不能用 set”,而是“你的数据有没有天然序、是否允许重排、重复判定逻辑是否真能用 表达”。没想清楚这点,代码跑起来才开始踩坑。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











