C++ set 使用完全指南 概述 std::set 是 C++ STL 中的有序集合容器,具有以下核心特性:
元素唯一性 :不会存储重复元素
自动排序 :元素默认按升序排列(可自定义规则)
对数复杂度 :所有查找、插入、删除操作均为 O(log n)
红黑树实现 :底层基于平衡二叉搜索树
与之对应的 std::unordered_set 基于哈希表实现,元素无序但平均操作复杂度为 O(1)。
基本操作 头文件与声明 1 2 3 4 5 6 7 8 9 10 11 #include <set> using namespace std;set<int > s1; set<int , greater<int >> s2; set<int > s3 = {1 , 3 , 5 , 2 , 4 };
插入元素 1 2 3 4 5 6 7 8 9 10 11 s.insert (10 ); s.emplace (20 ); s.insert ({1 , 2 , 3 }); auto result = s.insert (10 );if (result.second) { cout << "插入成功,位置:" << *result.first << endl; } else { cout << "元素已存在" << endl; }
删除元素 1 2 3 4 s.erase (5 ); s.erase (s.begin ()); s.erase (it1, it2); s.clear ();
查找元素 1 2 3 4 5 6 7 8 9 auto it = s.find (10 ); if (it != s.end ()) { cout << "找到:" << *it << endl; } if (s.count (10 )) { cout << "元素存在" << endl; }
边界查询 1 2 3 4 5 6 7 8 9 10 set<int > s = {2 , 4 , 6 , 8 , 12 , 16 , 24 , 32 }; auto lb = s.lower_bound (10 ); auto ub = s.upper_bound (10 ); auto range = s.equal_range (10 );
大小与状态 1 2 cout << "元素个数:" << s.size () << endl; cout << "是否为空:" << s.empty () << endl;
复杂度分析
insert(): O(log n)
erase(): O(log n)
find(): O(log n)
lower_bound(): O(log n)
count(): O(log n)
遍历:O(n)
遍历与有序查询 基本遍历方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 for (auto it = s.begin (); it != s.end (); ++it) { cout << *it << " " ; } for (int x : s) { cout << x << " " ; } for (auto it = s.rbegin (); it != s.rend (); ++it) { cout << *it << " " ; }
范围查询(利用有序性) 1 2 3 4 5 6 7 auto left = s.upper_bound (5 ); auto right = s.upper_bound (20 ); for (auto it = left; it != right; ++it) { cout << *it << " " ; }
集合运算 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <algorithm> set<int > a = {1 , 3 , 5 }; set<int > b = {2 , 3 , 4 }; vector<int > result; set_union (a.begin (), a.end (), b.begin (), b.end (), back_inserter (result));set_intersection (a.begin (), a.end (), b.begin (), b.end (), back_inserter (result));set_difference (a.begin (), a.end (), b.begin (), b.end (), back_inserter (result));set_symmetric_difference (a.begin (), a.end (), b.begin (), b.end (), back_inserter (result));
高级用法:自定义比较器 基本自定义比较器 1 2 3 4 5 6 7 8 9 struct Descending { bool operator () (int a, int b) const { return a > b; } }; set<int , Descending> s = {1 , 3 , 2 , 5 , 4 };
结构体集合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 struct Student { int id; string name; int score; }; struct StudentCmp { bool operator () (const Student& a, const Student& b) const { if (a.score != b.score) { return a.score > b.score; } return a.id < b.id; } }; set<Student, StudentCmp> students; students.insert ({3 , "Li" , 90 }); students.insert ({1 , "Zhang" , 95 }); students.insert ({2 , "Wang" , 95 });
严格弱序要求 自定义比较器必须满足严格弱序(Strict Weak Ordering):
非自反性:comp(a, a) 为 false
不对称性:若 comp(a, b) 为 true,则 comp(b, a) 为 false
传递性:若 comp(a, b) 且 comp(b, c),则 comp(a, c)
等价传递:若 !comp(a, b) && !comp(b, a) 且 !comp(b, c) && !comp(c, b),则 !comp(a, c) && !comp(c, a)
与 unordered_set 的对比
特性
set(有序集合)
unordered_set(哈希集合)
底层结构
红黑树(平衡BST)
哈希表
元素顺序
自动排序
无序(插入顺序不保证)
时间复杂度
查找/插入/删除:O(log n)
平均 O(1),最坏 O(n)
内存占用
较低(每个节点额外指针)
较高(哈希桶和负载因子)
迭代器稳定性
插入/删除不使其他迭代器失效
重新哈希会使所有迭代器失效
特殊操作
支持 lower_bound()、upper_bound()
不支持有序查询
选择建议
使用 set 的场景:
需要元素自动排序
需要范围查询或边界查找
需要按顺序遍历
元素数量不大或对最坏性能有要求
使用 unordered_set 的场景:
只需快速查找存在性
不需要元素有序
大量数据且哈希函数良好
对平均性能要求高
1 2 3 4 5 6 7 8 9 10 #include <unordered_set> set<int > ordered_set = {8 , 4 , 16 , 2 , 24 , 12 }; unordered_set<int > unordered_set = {8 , 4 , 16 , 2 , 24 , 12 }; cout << "set (有序): " ; for (int x : ordered_set) cout << x << " " ; cout << "\nunordered_set (无序): " ; for (int x : unordered_set) cout << x << " " ;
常见问题与最佳实践 1. 遍历时安全删除 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 set<int > s = {1 , 2 , 3 , 4 , 5 }; for (auto it = s.begin (); it != s.end (); ++it) { if (*it % 2 == 0 ) { s.erase (it); } } for (auto it = s.begin (); it != s.end (); ) { if (*it % 2 == 0 ) { it = s.erase (it); } else { ++it; } }
2. 元素不可变性 1 2 3 4 5 6 7 8 9 10 11 12 13 set<int > s = {1 , 2 , 3 , 4 , 5 }; for (int & x : s) { x = x * 2 ; } auto it = s.find (3 );if (it != s.end ()) { s.erase (it); s.insert (6 ); }
3. 性能优化 1 2 3 4 5 6 7 8 9 10 11 12 13 vector<int > data = {5 , 3 , 1 , 4 , 2 }; set<int > s1; for (int x : data) s1. insert (x);set<int > s2 (data.begin(), data.end()) ;sort (data.begin (), data.end ());set<int > s3 (data.begin(), data.end()) ;
4. 与 vector 互转 1 2 3 4 5 6 7 set<int > s = {3 , 1 , 4 , 1 , 5 , 9 }; vector<int > v (s.begin(), s.end()) ; vector<int > data = {3 , 1 , 4 , 1 , 5 , 9 , 2 , 6 }; set<int > s2 (data.begin(), data.end()) ;
实战示例 示例1:两集合乘积去重排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 vector<int > productSet (const vector<int >& A, const vector<int >& B) { set<int > result; for (int a : A) { for (int b : B) { result.insert (a * b); } } return vector <int >(result.begin (), result.end ()); } int main () { vector<int > A = {2 , 4 , 8 }; vector<int > B = {1 , 2 , 3 , 4 }; auto products = productSet (A, B); for (int p : products) cout << p << " " ; return 0 ; }
示例2:范围统计系统 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class RangeCounter {private : set<int > points; public : void addPoint (int x) { points.insert (x); } int countInRange (int l, int r) { auto left = points.lower_bound (l); auto right = points.upper_bound (r); return distance (left, right); } vector<int > getPointsInRange (int l, int r) { vector<int > result; auto left = points.lower_bound (l); auto right = points.upper_bound (r); for (auto it = left; it != right; ++it) { result.push_back (*it); } return result; } };
示例3:最近邻查找 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 pair<int , int > findNearest (set<int >& s, int target) { if (s.empty ()) return {INT_MIN, INT_MIN}; auto it = s.lower_bound (target); int right = (it != s.end ()) ? *it : INT_MIN; int left = (it != s.begin ()) ? *prev (it) : INT_MIN; return {left, right}; } int main () { set<int > s = {1 , 4 , 7 , 10 , 15 }; auto nearest = findNearest (s, 8 ); nearest = findNearest (s, 20 ); }
总结 std::set 是 C++ 中功能强大的有序集合容器:
适用场景 :需要元素唯一且有序的场景,如去重排序、范围查询、最近邻查找等
性能特点 :
所有操作 O(log n) 时间复杂度
适合元素数量适中(数千到数百万)
对最坏情况性能有保障
使用建议 :
优先使用 emplace() 而非 insert() 减少拷贝
利用有序性进行范围查询和边界查找
避免在遍历时直接修改元素
根据需求选择 set 或 unordered_set
扩展学习 :
multiset:允许重复元素的有序集合
map:键值对的有序映射
unordered_map:哈希映射
掌握 set 的用法能够极大提升处理有序唯一数据的能力,是 C++ 开发者的必备技能之一。
Author:
Lyxy-szs
Permalink:
http://lyxy-szs.github.io/2025/12/02/set/
Slogan:
永远相信美好的事情即将發生 ?