跳到主要内容C++ STL 有序关联容器详解:set、map 及其变体用法 | 极客日志C++算法
C++ STL 有序关联容器详解:set、map 及其变体用法
C++ STL 有序关联容器包括 set、multiset、map 和 multimap,基于红黑树实现,支持高效查找与排序。set 存储唯一键,multiset 允许重复;map 存储键值对,multimap 支持重复键。核心操作涵盖构造、插入、删除、查找及区间遍历。insert 返回 pair 指示是否插入成功,erase 返回后继迭代器。operator[] 在 map 中兼具查找与修改功能。掌握这些容器能显著提升数据处理效率,避免手动实现带来的性能瓶颈。
baireiraku0 浏览 C++ STL 有序关联容器详解
在 C++ 中,如果需要快速查找一组数据中的唯一元素,或者统计某个键出现的次数,手写数组或列表往往代码冗长且效率低下。STL(标准模板库)中的关联容器——set、multiset、map 和 multimap,正是为解决这类问题而生的。
1. set 容器

常用接口说明
1.1 构造函数
最常用的构造方式有三种,分别支持迭代器区间、拷贝以及初始化列表:
void test01() {
vector<int> v({ 4,5,2,9,6,8,3,1,7,7,7,7 });
set<int> st1(v.begin(), v.end());
set<int> st2(st1);
set<int> st3({ 4,5,2,,,,,,,,, });
}
9
6
8
3
1
7
7
7
7
1.2 迭代器
begin() 和 end() 是最常用的迭代器接口,常用于遍历或作为区间构造的参数。由于 set 底层是平衡二叉搜索树(红黑树),begin() 返回的迭代器指向最小的元素。
void test02() {
set<int> st({ 4,5,2,9,6,8,3,1,7,7,7,7 });
auto begin = st.begin();
auto end = st.end();
cout << *begin << endl;
cout << *(--end) << endl;
}
为了方便后续观察容器状态,我们可以封装一个简单的打印函数。
1.3 插入 insert
set 底层基于红黑树,插入数据遵循二叉搜索树规则,但不允许插入相同的值。insert 接口的返回值比较特殊,是一个 pair 对象:
first: 迭代器。如果插入成功,指向新节点;如果已存在,指向原节点。
second: 布尔值。插入成功为 true,失败为 false。
void test03() {
set<int> st;
pair<set<int>::iterator, bool> p1, p2;
p1 = st.insert(5);
st.insert(3);
p2 = st.insert(5);
cout << *(p1.first) << endl;
if (p2.second) cout << "原来不存在" << endl;
else cout << "原来存在" << endl;
}
1.4 删除 erase
删除操作同样返回后继迭代器,被删除节点的迭代器会失效。
void test04() {
set<int> st({ 4,5,2,9,6,8,3,1,7,7,7,7 });
Print_container(st);
set<int>::iterator it = st.erase(st.begin());
cout << *it << endl;
}
1.5 查找 find
找到则返回节点迭代器,未找到返回 set::end()。
void test05() {
set<int> st({ 4,5,2,9,6,8,3,1,7,7,7,7 });
auto it = st.find(5);
if(it != st.end()) {
cout << *it << endl;
}
}
1.6 统计 count
由于 set 不允许重复值,count 要么返回 1,要么返回 0。因此它也可以用来判断元素是否存在。
void test06() {
set<int> st({ 4,5,2,9,6,8,3,1,7,7,7,7 });
cout << st.count(5) << endl;
cout << st.count(50) << endl;
}
1.7 区间查找 lower_bound / upper_bound
这两个接口非常强大,可以根据不存在的值来确定区间范围。
void test07() {
set<int> st1({ 20,10,50,30,10,90,70,40,80 });
Print_container(st1);
auto begin1 = st1.find(30);
auto end1 = st1.find(50);
while (begin1 != end1) {
begin1 = st1.erase(begin1);
}
Print_container(st1);
set<int> st2({ 20,10,50,30,10,90,70,40,80 });
Print_container(st2);
auto begin2 = st2.lower_bound(30);
auto end2 = st2.upper_bound(50);
while (begin2 != end2) {
begin2 = st2.erase(begin2);
}
Print_container(st2);
}
2. multiset 容器
multiset 与 set 的主要区别在于支持插入相同的值。因此部分接口行为有所差异:
- 查找:
find 返回中序遍历序列中的第一个匹配项。
- 删除:调用
erase(val) 会一次性删除所有值为 val 的节点。
- 统计:
count 可能返回大于 1 的值。
常用接口说明
2.1 插入
void test1() {
multiset<int> multi;
multi.insert(3);
multi.insert(2);
multi.insert(3);
multi.insert(3);
Print_container(multi);
}
2.2 查找
当存在多个相同值时,find 仅返回第一个。若需遍历所有相同值,可结合 equal_range 使用。
2.3 删除
与 set 类似,但针对值的删除会移除所有匹配项。
2.4 统计
void test4() {
multiset<int> multi;
multi.insert(3);
multi.insert(3);
multi.insert(3);
multi.insert(3);
Print_container(multi);
cout << multi.count(3);
}
2.5 equal_range
3. map 容器
map 的常用接口逻辑与 set 基本一致,只是操作对象从单一键值变成了键值对(Key-Value)。查找、删除、统计及区间查找均基于键值完成。
常用接口说明
3.1 构造函数
3.2 插入 insert
map 插入时需要提供键值和映射值。利用 pair 可以方便地构造数据:
void test01() {
map<string, string> dict1;
pair<string, string> p("first", "第一");
dict1.insert(p);
dict1.insert(pair<string, string>("second", "第二"));
dict1.insert({ "third", "第三" });
dict1.insert(make_pair("forth", "第四"));
map<string, string> dict2 = {
{"left", "左边"},
{"right", "右边"},
{"insert", "插入"},
{"string", "字符串"}
};
Print_Container(dict1);
Print_Container(dict2);
}
注意:当插入新节点的键值已存在时,映射值不会更新。
3.3 operator[]
operator[] 功能强大,兼具查找、插入和修改映射值的能力。若键不存在则自动插入默认值,若存在则直接修改映射值。其返回值是映射值的引用。
void test_4() {
map<string, string> dict;
dict.insert({ "left", "左边" });
dict.insert(make_pair("right", "右边"));
dict["sort"];
dict["string"] = "字符串";
dict["left"] = "左边的";
auto it = dict["left"];
cout << it << endl;
}
常见场景:统计字符串中元素出现次数。将元素作为键,计数作为映射值,利用 operator[] 的特性即可轻松实现。
4. multimap 容器
multimap 几乎与 map 一样,区别在于支持插入相同键值的节点。
常用接口说明
4.1 插入
void Test01() {
multimap<string, string> mp;
mp.insert(make_pair("left", "左边"));
mp.insert(make_pair("left", "左边"));
mp.insert(make_pair("left", "左边"));
Print_Container(mp);
}
4.2 删除
void Test02() {
multimap<string, string> mp;
mp.insert(make_pair("left", "左边"));
mp.insert(make_pair("left", "左边"));
mp.insert(make_pair("left", "左边"));
mp.erase("left");
Print_Container(mp);
}
掌握这些容器的特性,能让我们在处理复杂数据结构时更加得心应手,避免重复造轮子。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
- JSON 压缩
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online