1. set:
1. 核心作用
set(集合) 是 C++ STL 中的关联容器,用于存储唯一、有序的元素。主要作用包括:
-
去重存储:自动过滤重复元素
-
自动排序:元素按指定顺序自动排列
-
快速检索:基于平衡二叉搜索树(通常是红黑树)实现 O(log n) 的查找
-
范围查询:支持查找特定范围内的元素
-
集合运算:支持交集、并集、差集等数学集合操作
2. 关键特性
| 特性 | 描述 |
|---|---|
| 唯一性 | 容器内元素唯一,不允许重复 |
| 有序性 | 元素自动排序(默认升序) |
| 不可修改 | 元素值不可修改(会破坏树结构) |
| 动态大小 | 大小可根据需要动态变化 |
| 双向迭代器 | set提供双向迭代器,可以向前和向后遍历,但不支持随机访问。 |
3. 底层实现
// set 底层通常实现为红黑树(平衡二叉搜索树)
template <typename Key,
typename Compare = std::less<Key>,
typename Allocator = std::allocator<Key>>
class set {
// 底层是红黑树,通常是 std::_Rb_tree
};
4. 接口函数:
-
构造函数:
// 空set,使用默认的比较函数和分配器
set<Key, Compare, Allocator> set1;
// 空set,指定比较函数comp
set<Key, Compare, Allocator> set2(comp);
// 拷贝构造
set<Key, Compare, Allocator> set3(set1);
// 用迭代器范围构造
set<Key, Compare, Allocator> set4(set1.begin(), set1.end());// 默认构造函数(升序) set<int> s1; // 初始化列表 set<int> s2 = {3, 1, 4, 1, 5, 9}; // {1, 3, 4, 5, 9} // 复制构造函数 set<int> s3(s2); // 迭代器范围初始化 int arr[] = {2, 4, 6, 8}; set<int> s4(arr, arr + 4); // 自定义排序规则(降序) set<int, greater<int>> s5 = {5, 2, 8, 1}; // {8, 5, 2, 1} -
赋值操作:
set1 = set2; // 赋值运算符 set1 = {value1, value2, ...}; // 初始化列表赋值 -
迭代器:
begin() / cbegin():返回指向第一个元素的迭代器
end() / cend():返回指向末尾(最后一个元素之后)的迭代器
rbegin() / crbegin():返回指向第一个元素的反向迭代器
rend() / crend():返回指向末尾的反向迭代器
c = const;表示此迭代器指向的内容不可修改set<int> s = {1, 3, 5, 7, 9}; // 正向迭代器(升序遍历) for (auto it = s.begin(); it != s.end(); ++it) { cout << *it << " "; // 1 3 5 7 9 } // 反向迭代器(降序遍历) for (auto rit = s.rbegin(); rit != s.rend(); ++rit) { cout << *rit << " "; // 9 7 5 3 1 } // 范围for循环(C++11) for (const auto& val : s) { cout << val << " "; } -
容量:
empty():检查是否为空
size():返回元素数量
max_size():返回可容纳的最大元素数量 -
修改器:
insert(const value_type& value):插入一个元素,返回一个pair,包含一个迭代器(指向插入的元素)和一个bool(表示是否插入成功)
insert(value_type&& value):移动插入
insert(iterator hint, const value_type& value):在hint位置附近插入,可能提高效率
insert(InputIt first, InputIt last):插入一个范围
emplace(args...):直接构造元素,避免拷贝
emplace_hint(hint, args...):在hint位置附近直接构造
erase(iterator pos):删除迭代器指向的元素
erase(iterator first, iterator last):删除一个范围
erase(const key_type& key):删除键为key的元素,返回删除的元素个数(0或1)
swap(set2):交换两个set
clear():清空所有元素set<int> s = {10, 20, 30, 40, 50}; // 插入单个元素 s.insert(10); s.insert(20); s.insert(10); // 重复元素,插入失败 // 插入带返回值 auto result = s.insert(30); // 返回 pair<iterator, bool> // result.first -> 指向插入元素的迭代器 // result.second -> 插入成功与否(true/false) // 插入多个元素 s.insert({40, 50, 60}); // 使用迭代器提示插入位置(效率提示) auto it = s.begin(); s.insert(it, 25); // it 只是提示,不一定插入在该位置 // 通过值删除(返回删除的元素个数) size_t count = s.erase(20); // count = 1 // 通过迭代器删除 auto it = s.find(30); if (it != s.end()) { s.erase(it); // 删除30 } // 删除一个范围 auto first = s.find(40); auto last = s.end(); s.erase(first, last); // 删除40和50 // 清空所有元素 s.clear(); -
查找:
count(const key_type& key):返回键等于key的元素个数(0或1,因为唯一性)
find(const key_type& key):返回指向键等于key的元素的迭代器,如果没找到则返回end()
equal_range(const key_type& key):返回一个pair,包含两个迭代器,表示键等于key的元素范围(对于set,这个范围最多包含一个元素)
lower_bound(const key_type& key):返回指向第一个键>=key的元素的迭代器
upper_bound(const key_type& key):返回指向第一个键>key的元素的迭代器set<int> s = {10, 20, 30, 40, 50}; // find() - O(log n) auto it = s.find(30); // 返回指向30的迭代器,找不到返回 s.end() // count() - O(log n) int exists = s.count(20); // 返回1(存在)或0(不存在) // lower_bound() - 第一个不小于key的元素 auto lb = s.lower_bound(25); // 指向30 // upper_bound() - 第一个大于key的元素 auto ub = s.upper_bound(25); // 指向30 // equal_range() - 返回等于key的范围(对set意义不大,总是0或1个元素) auto range = s.equal_range(30); // 返回 pair<iterator, iterator> -
观察器:
key_comp():返回比较键的函数对象 value_comp():返回比较值的函数对象(对于set,key和value是一样的) //用这两个函数获取你的set元素排序比较的逻辑函数 -
非成员函数:
operator==, !=, <, <=, >, >=:比较两个set swap(set1, set2):交换两个set
2. multiset:
multiset是关联容器,类似于set,但允许重复元素。它存储的元素按照特定的排序准则自动排序,默认是升序。multiset通常也基于红黑树实现。
主要特性:
-
允许重复元素。
-
自动排序(默认升序,可自定义比较函数)。
-
插入、删除和查找操作的时间复杂度为O(log n)。
-
迭代器是双向迭代器。
与set的主要区别:
-
set中元素必须唯一,而multiset允许重复。
-
multiset的insert操作总是成功的(因为可以重复),返回指向插入元素的迭代器。
-
在multiset中,count()函数可能返回大于1的值。
详细差异:
由于multiset允许重复元素,所以一些操作的行为会有所不同,例如:
-
insert: 总是插入,返回指向新插入元素的迭代器。
-
erase: 删除指定元素,返回删除的元素个数(可能大于1)。
-
count: 返回指定元素在容器中的个数。
-
find: 返回指向第一个等于给定值的元素的迭代器。
-
equal_range: 返回一个pair,表示等于给定值的元素范围。
| 对比维度 | set | multiset |
|---|---|---|
| 元素唯一性 | 必须唯一 | 允许重复 |
| 插入返回值类型 | pair<iterator, bool> | iterator |
| 插入行为 | 重复元素插入失败 | 总是插入成功 |
| count() 返回值 | 0 或 1 | 0 到 n |
| erase(value) 返回值 | 0 或 1 | 删除的元素个数(0到n) |
| equal_range() 用途 | 形式上的(返回单个元素或无) | 实际获取重复元素范围 |
| 内存占用 | 每个值只存储一次 | 每个值可能存储多次 |
| 底层节点数 | 等于唯一元素个数 | 等于总插入元素个数 |
1. 插入操作差异
// ==================== set ====================
set<int> s;
auto result1 = s.insert(10); // 返回: pair<iterator, bool>
// result1.first -> 迭代器
// result1.second -> true (插入成功)
auto result2 = s.insert(10); // 返回: pair<iterator, bool>
// result2.first -> 指向已存在10的迭代器
// result2.second -> false (插入失败)
// ==================== multiset ====================
multiset<int> ms;
auto it1 = ms.insert(10); // 返回: iterator
// it1 -> 指向新插入的10
auto it2 = ms.insert(10); // 返回: iterator
// it2 -> 指向第二个10 (总是成功)
差异总结表:
| 特性 | set::insert() | multiset::insert() |
|---|---|---|
| 返回值类型 | pair<iterator, bool> | iterator |
| 返回值.first | 指向插入位置的迭代器 | 指向新插入元素的迭代器 |
| 返回值.second | 插入成功与否(true/false) | 无此成员 |
| 插入重复元素 | 失败,返回false | 成功,创建新节点 |
| 时间复杂度 | O(log n) | O(log n) |
2. 删除操作差异
// ==================== set ====================
set<int> s = {10, 20, 30};
size_t n1 = s.erase(20); // n1 = 1
size_t n2 = s.erase(20); // n2 = 0 (元素不存在)
size_t n3 = s.erase(30); // n3 = 1
// ==================== multiset ====================
multiset<int> ms = {10, 20, 20, 30, 30, 30};
size_t m1 = ms.erase(20); // m1 = 2 (删除所有20)
size_t m2 = ms.erase(20); // m2 = 0 (元素不存在)
size_t m3 = ms.erase(30); // m3 = 3 (删除所有30)
差异总结表:
| 特性 | set::erase(value) | multiset::erase(value) |
|---|---|---|
| 返回值含义 | 删除的元素个数 | 删除的元素个数 |
| 返回值范围 | 0 或 1 | 0 到 n |
| 删除行为 | 删除找到的元素(最多1个) | 删除所有匹配的元素 |
| 通过迭代器删除 | 删除单个元素 | 删除单个元素 |
3. 查找和计数差异
// ==================== set ====================
set<int> s = {10, 20, 30};
int c1 = s.count(20); // c1 = 1
int c2 = s.count(40); // c2 = 0
bool exists = c1 > 0; // 判断存在性
// ==================== multiset ====================
multiset<int> ms = {10, 20, 20, 30, 30, 30};
int m1 = ms.count(20); // m1 = 2
int m2 = ms.count(30); // m2 = 3
int m3 = ms.count(40); // m3 = 0
差异总结表:
| 特性 | set::count() | multiset::count() |
|---|---|---|
| 返回值含义 | 元素是否存在 | 元素出现次数 |
| 返回值范围 | 0 或 1 | 0 到 n |
| 时间复杂度 | O(log n) | O(log n + k),其中k是重复次数 |
| 主要用途 | 检查存在性 | 统计频率 |
4. 范围查询差异
// ==================== set ====================
set<int> s = {10, 20, 30, 40, 50};
auto range1 = s.equal_range(30);
// range1.first -> 指向30
// range1.second -> 指向40
// distance(range1.first, range1.second) = 1 (总是0或1)
// ==================== multiset ====================
multiset<int> ms = {10, 20, 20, 30, 30, 30, 40};
auto range2 = ms.equal_range(30);
// range2.first -> 指向第一个30
// range2.second -> 指向40 (最后一个30的下一个位置)
// distance(range2.first, range2.second) = 3
差异总结表:
| 特性 | set::equal_range() | multiset::equal_range() |
|---|---|---|
| 实际意义 | 形式上的,总是0或1个元素 | 真正的"相等范围",可能有多个元素 |
| 使用频率 | 较少使用(find足够) | 经常使用(处理重复元素) |
| 与count关系 | count() ∈ {0, 1} | count() = distance(first, second) |
| 典型用途 | 获取单个元素的迭代器 | 遍历所有重复元素 |
5. 迭代器遍历差异
// ==================== set ====================
set<int> s = {10, 20, 20, 30}; // 实际存储 {10, 20, 30}
for (int val : s) {
// 遍历:10, 20, 30 (每个值只出现一次)
}
// ==================== multiset ====================
multiset<int> ms = {10, 20, 20, 30}; // 存储 {10, 20, 20, 30}
for (int val : ms) {
// 遍历:10, 20, 20, 30 (保留所有插入)
}
6. 唯一性保证
// set - 自动去重
set<int> s;
s.insert(10);
s.insert(10);
s.insert(10);
// s.size() = 1, 只包含一个10
// multiset - 保留所有
multiset<int> ms;
ms.insert(10);
ms.insert(10);
ms.insert(10);
// ms.size() = 3, 包含三个10
7. 查找稳定性
// set - find() 返回唯一元素的迭代器
set<int> s = {10, 20, 30};
auto it = s.find(20); // 总是找到同一个20
// multiset - find() 返回第一个匹配元素的迭代器
multiset<int> ms = {10, 20, 20, 20, 30};
auto mit = ms.find(20); // 找到第一个20,不是随机的
8. 删除稳定性
multiset<int> ms = {10, 20, 20, 20, 30};
// 删除特定位置的20
auto it = ms.find(20);
if (it != ms.end()) {
ms.erase(it); // 只删除第一个20
}
// 删除所有20
ms.erase(20); // 删除剩余的两个20
性能差异详表
| 操作 | set 复杂度 | multiset 复杂度 | 差异说明 |
|---|---|---|---|
| insert(value) | O(log n) | O(log n) | set需要检查重复,multiset直接插入 |
| erase(value) | O(log n + 1) | O(log n + k) | multiset需要删除所有k个重复元素 |
| erase(iterator) | O(1) 平均 | O(1) 平均 | 相同 |
| find(value) | O(log n) | O(log n) | 相同 |
| count(value) | O(log n) | O(log n + k) | multiset需要遍历重复元素 |
| lower_bound() | O(log n) | O(log n) | 相同 |
| upper_bound() | O(log n) | O(log n) | 相同 |
| equal_range() | O(log n) | O(log n) | 相同 |
1424

被折叠的 条评论
为什么被折叠?



