
为什么std::set天然去重
std::set底层是红黑树(平衡二叉搜索树),插入时自动按operator<比较元素,相同元素(即a < b和b < a都为false)视为等价,直接忽略后续插入。这不是“事后过滤”,而是插入阶段就拒绝重复。
注意:去重依赖operator<的严格弱序定义。若自定义类型没正确定义它,或用std::set<int*, std::greater<>>这类带比较器的变体,行为可能不符合直觉。
插入数据时自动去重的写法
直接用insert()或初始化列表,无需额外逻辑:
std::set<int> s = {1, 2, 2, 3, 3, 1}; // 结果:{1, 2, 3}
s.insert(2); // 无效果
s.insert(4); // 成功插入如果已有std::vector等容器需去重:
立即学习“C++免费学习笔记(深入)”;
- 用迭代器构造:
std::set<int> s(vec.begin(), vec.end()); - 逐个
insert():适合需要判断是否真正插入(insert()返回std::pair<iterator, bool>) - 避免先
clear()再循环insert()——构造新set通常更简洁
与std::unordered_set的关键区别
两者都去重,但机制和代价不同:
-
std::set:有序、O(log n)插入/查找、无需哈希函数、支持范围遍历(如lower_bound) -
std::unordered_set:无序、均摊O(1)操作、要求类型可哈希(需std::hash<T>特化)、不保证遍历顺序 - 若只需去重+快速查存在,且类型已支持哈希(如
int、std::string),unordered_set更快;若后续要取第k小、遍历有序序列,必须用set
常见错误:误以为set能去重指针地址值
这是高频陷阱。下面代码不会去重:
std::set<int*> s; int a = 1, b = 1; s.insert(&a); s.insert(&b); // 插入成功!因为&a != &b
原因:int*的默认比较是地址大小,不是所指内容。解决方式:
- 存值而非指针:
std::set<int> - 自定义比较器,按内容比较:
std::set<int*, decltype(cmp)> s(cmp),其中cmp是[](int* x, int* y) { return *x < *y; } - 用
std::set<std::reference_wrapper<const int>>(需确保引用对象生命周期足够长)
去重逻辑永远绑定在比较语义上,不是数据本身“看起来一样”就自动合并——这点比想象中更关键,也更容易被忽略。


















