std::set
- 1. 概述
- 定义
- 特点
 
- 2. 内部实现
- 3. 性能特征
- 4. 常用 API
- 5. 使用示例
- 6. 自定义比较器
- 7. 注意事项与优化
- 8. 使用建议
1. 概述
定义
template<class Key,class Compare = std::less<Key>,class Allocator = std::allocator<Key>
>
class std::set;
特点
-  有序:所有元素按严格弱序(由 Compare 决定)排列,不保证相同键的重复出现(键唯一)。 
-  唯一键:插入时如果集合中已有相同元素,不会增加新的节点。 
-  底层结构:通常基于红黑树(balanced binary search tree)实现。 
2. 内部实现
-  红黑树 - 平衡二叉查找树,保证从根到任一叶子路径的“黑色节点数”相同,从而令树高度为 O(log n)。
 
-  节点结构 - 每个节点存储: - 元素值 Key
- 左/右子指针和父指针
- 一个颜色标记(红或黑)
 
 
- 每个节点存储: 
-  拷贝与移动 - 拷贝时会对整棵树进行深拷贝;移动构造则接管底层指针,无需逐节点复制。
 
3. 性能特征
| 操作 | 平均复杂度 | 最坏复杂度 | 备注 | 
|---|---|---|---|
| insert | O(log n) | O(log n) | 包括查找插入位置与调整平衡 | 
| erase | O(log n) | O(log n) | 按键删除时需旋转与重着色 | 
| find | O(log n) | O(log n) | |
| clear | O(n) | O(n) | 释放所有节点 | 
| 遍历 | O(n) | O(n) | 中序遍历即可 | 
-  内存开销: - 每个节点需存储三个指针(左右、父)和一个颜色字段,相比哈希表更紧凑但比 std::vector 等顺序容器要高。
 
-  迭代器失效规则: - 插入和删除会使仅被删除的节点的迭代器失效,其他节点迭代器保持有效。
 
4. 常用 API
// 构造与析构
std::set<Key> s1;                             // 默认构造
std::set<Key> s2({k1, k2, k3});               // 初始化列表
std::set<Key, Compare> s3(comp);              // 指定比较器// 大小与容量
bool empty() const noexcept;
size_t size() const noexcept;
size_t max_size() const noexcept;// 元素访问
iterator find(const Key& key);
size_t count(const Key& key) const;           // 要么 0,要么 1// 插入
std::pair<iterator,bool> insert(const Key& key);
iterator insert(iterator hint, const Key& key);
template<class... Args>
std::pair<iterator,bool> emplace(Args&&... args);// 删除
size_t erase(const Key& key);
iterator erase(iterator pos);
iterator erase(iterator first, iterator last);// 遍历
iterator begin() noexcept;
iterator end() noexcept;
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;// 有序查询
iterator lower_bound(const Key& key);         // ≥ key 的第一个位置
iterator upper_bound(const Key& key);         // > key 的第一个位置
std::pair<iterator,iterator> equal_range(const Key& key);// 比较器与分配器访问
Compare key_comp() const;
Compare value_comp() const;                   // 同 key_comp
Allocator get_allocator() const noexcept;// 交换
void swap(std::set& other) noexcept;
5. 使用示例
#include <set>
#include <iostream>int main() {std::set<int> s;// 插入s.insert(3);s.emplace(1);s.insert({5, 2, 4});// 查找if (s.find(2) != s.end()) {std::cout << "2 存在\n";}// 遍历(中序:1,2,3,4,5)for (int x : s) {std::cout << x << " ";}std::cout << "\n";// 有序查询auto it = s.lower_bound(3);  // 指向 3std::cout << "lower_bound(3): " << *it << "\n";// 删除s.erase(3);                  // 删除值为 3 的节点// 范围删除s.erase(s.begin(), s.lower_bound(4)); // 删除所有 <4 的元素// 清空s.clear();
}
6. 自定义比较器
当需要自定义排序规则(如降序或复合键)时,可提供自定义比较器:
struct Desc {bool operator()(int a, int b) const {return a > b;  // 降序}
};std::set<int, Desc> s_desc;  // 插入后,将按从大到小顺序存储
对于复合类型:
struct Person {std::string name;int age;
};struct ByAgeName {bool operator()(Person const& a, Person const& b) const {if (a.age != b.age) return a.age < b.age;return a.name < b.name;}
};// 年龄升序,若年龄相同则按名字升序
std::set<Person, ByAgeName> roster;
7. 注意事项与优化
-  避免不必要的拷贝 - insert(const Key&) 会拷贝一次;若可移动,优先使用 insert(Key&&) 或 emplace()。
 
-  hint 参数 - insert(hint, key):若能提供一个接近正确位置的迭代器 hint,可将插入复杂度降到常数时间。
 
-  批量插入 - 对初始化列表或范围插入,建议先 reserve()(C++23 起支持)或构造时传入范围,以减少重平衡次数。
 
-  避免迭代器失效 - 删除或插入仅影响相关节点迭代器,其他迭代器保持有效。
 
8. 使用建议
-  适用场景 - 需要自动排序且元素唯一时,首选 std::set。
- 需要查询前驱/后继、区间操作(lower_bound、upper_bound、中序遍历)时非常方便。
 
-  不适用场景 - 需要允许重复键或多对一映射时,改用 std::multiset 或 std::map。
- 对性能有极致要求且不在意顺序时,可考虑 std::unordered_set(哈希实现,平均 O(1))。