- 分享
stl(set)
- 2025-6-26 15:08:17 @
C++ std::set
有序集合指南
概述
std::set
是 C++ 标准库中的有序关联容器,存储唯一元素(自动去重),默认按升序排列。基于红黑树实现,提供高效的查找、插入和删除操作(O(log n) 复杂度)。
1. 包含头文件
#include <set>
2. 声明与初始化
初始化方式 | 示例 | 说明 |
---|---|---|
空集合 | std::set<int> mySet; |
创建空集合 |
初始化列表 | std::set<int> mySet = {3, 1, 4, 1, 5}; |
自动去重排序 → {1, 3, 4, 5} |
拷贝构造 | std::set<int> setCopy(mySet); |
复制已有集合 |
迭代器范围 | std::set<int> setRange(vec.begin(), vec.end()); |
从其他容器初始化 |
// 自定义排序规则(降序)
auto cmp = [](int a, int b) { return a > b; };
std::set<int, decltype(cmp)> descendingSet(cmp);
descendingSet = {5, 2, 8, 1}; // 排序后: {8, 5, 2, 1}
3. 核心操作
插入元素 (insert()
)
std::set<int> s;
// 插入单个元素
auto [pos1, inserted1] = s.insert(10); // C++17 结构化绑定
// 插入多个元素
s.insert({20, 30, 15});
// 插入结果处理
if (inserted1) {
std::cout << "10 inserted successfully\n";
}
// 使用迭代器提示位置(优化插入效率)
auto hint = s.lower_bound(25);
s.insert(hint, 25);
删除元素
方法 | 示例 | 说明 |
---|---|---|
erase(key) |
s.erase(20); |
删除值为20的元素 |
erase(iterator) |
s.erase(s.begin()); |
删除首个元素 |
erase(range) |
s.erase(s.find(15), s.end()); |
删除15到末尾 |
clear() |
s.clear(); |
清空整个集合 |
查找元素
方法 | 示例 | 说明 |
---|---|---|
find() |
auto it = s.find(30); |
返回迭代器(未找到返回end) |
count() |
if (s.count(25)) {...} |
存在返回1,否则0 |
contains() (C++20) |
if (s.contains(42)) {...} |
更直观的存在检查 |
lower_bound() |
auto lb = s.lower_bound(20); |
返回首个≥20的元素迭代器 |
upper_bound() |
auto ub = s.upper_bound(20); |
返回首个>20的元素迭代器 |
equal_range() |
auto [lb, ub] = s.equal_range(20); |
返回等于20的范围 |
访问元素
// 获取首元素(最小元素)
if (!s.empty()) {
int minVal = *s.begin(); // 注意:不是front()
}
// 获取尾元素(最大元素)
if (!s.empty()) {
int maxVal = *s.rbegin(); // 注意:不是back()
}
4. 集合遍历
顺序遍历(升序)
for (const auto& num : s) {
std::cout << num << " ";
}
// 输出: 10 15 25 30
逆序遍历(降序)
for (auto it = s.rbegin(); it != s.rend(); ++it) {
std::cout << *it << " ";
}
// 输出: 30 25 15 10
使用迭代器遍历
std::cout << "Set elements: ";
for (auto it = s.begin(); it != s.end(); ) {
std::cout << *it;
if (++it != s.end()) std::cout << " -> ";
}
// 输出: 10 -> 15 -> 25 -> 30
5. 容量与状态查询
方法 | 示例 | 说明 |
---|---|---|
size() |
size_t count = s.size(); |
返回元素数量 |
empty() |
if (s.empty()) {...} |
检查是否为空 |
max_size() |
size_t max = s.max_size(); |
返回理论最大容量 |
6. 特殊操作
交换两个集合
std::set<int> setA = {1, 2, 3};
std::set<int> setB = {4, 5, 6};
setA.swap(setB); // setA = {4,5,6}, setB = {1,2,3}
集合比较
std::set<int> setX = {1, 2, 3};
std::set<int> setY = {1, 2, 3};
if (setX == setY) { // 按元素顺序比较
std::cout << "Sets are equal\n";
}
7. 性能特性与注意事项
时间复杂度
操作 | 时间复杂度 | 说明 |
---|---|---|
插入 | O(log n) | 红黑树平衡操作 |
删除 | ||
查找 | 二叉树搜索 | |
遍历 | O(n) | 线性遍历所有节点 |
关键注意事项
-
元素唯一性:自动去重,插入重复值会被忽略
std::set<int> s = {1, 1, 2}; // 实际存储 {1, 2}
-
元素不可变性:集合元素为const,不能直接修改
// 错误示例(编译失败): // *s.begin() = 100; // 正确修改方式: int oldValue = 10; if (s.erase(oldValue)) { s.insert(100); // 先删除再插入新值 }
-
自定义类型支持:需提供比较函数
struct Point { int x, y; }; auto pointCmp = [](const Point& a, const Point& b) { return a.x < b.x || (a.x == b.x && a.y < b.y); }; std::set<Point, decltype(pointCmp)> pointSet(pointCmp);
8. 应用场景
-
数据去重:自动去除重复元素
std::vector<int> data = {5, 2, 5, 1, 3, 2}; std::set<int> uniqueData(data.begin(), data.end()); // uniqueData = {1, 2, 3, 5}
-
有序存储:自动维护排序状态
std::set<std::string> names = {"Charlie", "Alice", "Bob"}; // 自动排序: {"Alice", "Bob", "Charlie"}
-
快速查找:对数时间查找
// 比排序后的vector更适用于频繁插入/删除的场景 if (names.find("Bob") != names.end()) { // 找到元素 }
-
范围查询:高效获取区间元素
std::set<int> scores = {50, 65, 80, 90, 100}; auto low = scores.lower_bound(60); // >=60 auto high = scores.upper_bound(90); // >90 for (auto it = low; it != high; ++it) { std::cout << *it << " "; // 输出: 65 80 90 }
9. 与其他容器对比
特性 | std::set |
std::unordered_set |
std::vector |
---|---|---|---|
排序 | 有序 | 无序 | 可排序(需手动) |
查找复杂度 | O(log n) | O(1)平均 | O(n) |
插入/删除 | |||
内存布局 | 非连续 | 连续 | |
重复元素 | 自动去重 | 允许重复 | |
迭代器稳定性 | 稳定 | 插入可能失效 | 可能失效 |
选择建议:需要有序存储且频繁查找时用
std::set
;只需快速查找不关心顺序用std::unordered_set
;需要连续存储或随机访问用std::vector
。
0 条评论
目前还没有评论...