C++ STL 中的 set
和 multiset
容器详解 2
概述
在 C++ 标准模板库(STL)中,set
和 multiset
是两种非常重要的关联容器。它们都基于红黑树实现,能够自动对元素进行排序。本文将详细解析这两种容器的特性、用法和区别。
头文件引入
#include <set>
#include <algorithm>
#include <functional>
#include <iostream>
<set>
:包含set
和multiset
的定义<algorithm>
:提供各种算法操作<functional>
:包含函数对象和操作符<iostream>
:标准输入输出流
set
容器
基本特性
set
是一个有序的唯一键集合,具有以下特点:
- 自动排序:元素插入后会自动按升序排列
- 唯一性:不允许有重复元素
- 不可修改元素值:元素值一旦插入就不能修改,只能删除后重新插入
初始化与插入
set<int> setInt;
for (int i = 5; i > 0; i--) {
setInt.insert(i); // 插入元素
}
- 这里我们创建了一个
set<int>
容器setInt
- 使用
insert()
方法插入元素 - 虽然我们按 5,4,3,2,1 的顺序插入,但
set
会自动排序为 1,2,3,4,5
拷贝构造与赋值
set<int> setInt1(setInt); // 拷贝构造函数
set<int> setInt2(setInt1); // 另一个拷贝构造
setInt2.insert(666); // 插入新元素
setInt2.swap(setInt1); // 交换两个set的内容
set
支持拷贝构造函数,可以基于现有set
创建新set
swap()
方法可以高效交换两个set
的内容
遍历与输出
set<int>::iterator it = setInt1.begin();
for (; it != setInt1.end(); ++it) {
cout << *it << " ";
}
- 使用迭代器遍历
set
begin()
返回指向第一个元素的迭代器end()
返回指向最后一个元素之后的迭代器- 迭代器支持
++
操作符移动到下一个元素 - 使用
*
解引用迭代器获取元素值
multiset
容器
基本特性
multiset
与 set
类似,但有重要区别:
- 允许重复元素
- 同样保持元素有序
- 基于红黑树实现
基本操作
multiset<int> mulInt;
mulInt.insert(99); // 可以插入重复值
multiset
的接口与set
基本一致- 主要区别在于允许插入重复值
关键知识点总结
1. 自动排序
set
和 multiset
都会自动对元素进行排序,默认是升序排列。可以通过自定义比较函数改变排序方式。
2. 元素唯一性
set
:保证元素的唯一性,插入重复元素会被忽略multiset
:允许重复元素存在
3. 迭代器
- 使用双向迭代器,可以向前和向后遍历
- 迭代器遍历时按排序顺序访问元素
- 迭代器失效:只有被删除元素的迭代器会失效,其他迭代器不受影响
4. 常用操作
操作 | 描述 | 时间复杂度 |
---|---|---|
insert() | 插入元素 | O(log n) |
erase() | 删除元素 | O(log n) |
find() | 查找元素 | O(log n) |
size() | 返回元素数量 | O(1) |
empty() | 检查是否为空 | O(1) |
clear() | 清空容器 | O(n) |
5. 性能特点
由于基于红黑树实现,set
和 multiset
提供较好的综合性能:
- 查找、插入、删除都是 O(log n) 复杂度
- 元素有序存储,适合需要有序访问的场景
- 相比
unordered_set
,内存占用更小但访问速度稍慢
实际应用场景
- 需要有序且唯一元素的集合:使用
set
- 需要有序但允许重复的集合:使用
multiset
- 频繁查找的场景:两者都适合
- 需要按顺序处理的元素集合:如按分数排序的学生成绩
注意事项
set
中的元素是const的,不能直接修改- 插入自定义类型时需要提供比较函数
- 迭代器遍历时,元素是按排序顺序访问的
- 删除元素时要小心迭代器失效问题
完整代码回顾
#include <set>
#include <algorithm>
#include <functional>
#include <iostream>
using namespace std;
int main(void) {
// 创建并初始化set
set<int> setInt;
for (int i = 5; i > 0; i--) {
setInt.insert(i); // 插入元素,自动排序
}
// set的拷贝构造
set<int> setInt1(setInt);
set<int> setInt2(setInt1);
// 插入新元素和交换
setInt2.insert(666);
setInt2.swap(setInt1);
// 遍历输出
cout << "setInt1:" << endl;
for (auto it = setInt1.begin(); it != setInt1.end(); ++it) {
cout << *it << " ";
}
cout << "\nsetInt2:" << endl;
for (auto it = setInt2.begin(); it != setInt2.end(); ++it) {
cout << *it << " ";
}
// multiset演示
multiset<int> mulInt;
mulInt.insert(99); // 允许重复插入
system("pause");
return 0;
}