跳到主要内容C++ STL 有序关联容器:set、multiset、map 与 multimap 详解 | 极客日志C++算法
C++ STL 有序关联容器:set、multiset、map 与 multimap 详解
C++ STL 有序关联容器包含 set、multiset、map 和 multimap 四种类型,底层基于红黑树实现,支持高效查找、插入和删除。set 存储唯一键,multiset 允许重复;map 存储键值对,multimap 允许重复键。本文详解各容器的构造函数、迭代器操作及常用接口差异,重点讲解 insert 返回值含义、erase 迭代器失效规则以及 operator[] 在 map 中的特殊用法。
SqlMaster0 浏览 C++ STL 有序关联容器详解
在 C++ 开发中,当需要快速查找唯一元素或统计键值出现次数时,使用数组或列表手动实现往往代码冗长且效率低下。STL(标准模板库)中的关联容器——set、multiset、map 和 multimap,正是为解决这类问题而生的。它们底层通常基于红黑树实现,提供高效的插入、删除和查找操作。
set 容器
set 存储唯一的键值,自动排序。其常用接口如下:
构造函数
最常用的构造方式包括迭代器区间构造、拷贝构造和初始化列表构造:
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});
}
迭代器
返回指向最小元素的迭代器, 指向最后一个元素的后一个位置。由于 底层是平衡二叉搜索树,遍历时天然有序。
begin()
end()
set
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;
}
为了方便观察容器内容,后续示例中我们将直接使用循环打印,不再依赖外部辅助函数。
插入 insert
set 不允许插入重复值。insert 的返回值是一个 pair<iterator, bool> 对象:
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;
}
删除 erase
erase 支持通过迭代器或键值删除。通过迭代器删除时,函数会返回该节点后一个节点的迭代器,原迭代器失效。
void test04() {
set<int> st({4, 5, 2, 9, 6, 8, 3, 1, 7, 7, 7, 7});
set<int>::iterator it = st.erase(st.begin());
cout << *it << endl;
}
查找 find
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;
}
}
统计 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;
}
区间查找 lower_bound/upper_bound
void test07() {
set<int> st1({20, 10, 50, 30, 10, 90, 70, 40, 80});
auto begin1 = st1.find(30);
auto end1 = st1.find(50);
while (begin1 != end1) {
begin1 = st1.erase(begin1);
}
for (const auto& val : st1) cout << val << " ";
cout << endl;
set<int> st2({20, 10, 50, 30, 10, 90, 70, 40, 80});
auto begin2 = st2.lower_bound(30);
auto end2 = st2.upper_bound(50);
while (begin2 != end2) {
begin2 = st2.erase(begin2);
}
for (const auto& val : st2) cout << val << " ";
cout << endl;
}
multiset 容器
multiset 与 set 类似,但允许插入相同的值。因此部分接口行为有所不同:
- 插入:直接插入,不检查重复。
- 查找 find:返回中序遍历序列中的第一个匹配项。
- 删除 erase:若通过键值删除,会一次性删除所有具有相同值的节点;若通过迭代器删除,仅删除当前迭代器指向的节点。
- 统计 count:返回匹配到的元素总个数,可能大于 1。
- equal_range:返回包含所有相同值元素的迭代器区间。
void test1() {
multiset<int> multi;
multi.insert(3);
multi.insert(2);
multi.insert(3);
multi.insert(3);
for (const auto& val : multi) cout << val << " ";
cout << endl;
}
void test4() {
multiset<int> multi;
multi.insert(3);
multi.insert(3);
multi.insert(3);
multi.insert(3);
cout << multi.count(3);
}
map 容器
map 存储键值对(Key-Value),按键排序。其查找、删除、统计等操作逻辑与 set 一致,只是针对的是 Key。
构造函数
map<string, string> dict1;
map<string, string> dict2 = { {"left", "左边"}, {"right", "右边"} };
插入 insert
可以使用 pair 对象或初始化列表插入。注意,如果 Key 已存在,映射值不会更新。
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", "第四"));
for (const auto& kv : dict1) {
cout << kv.first << ": " << kv.second << endl;
}
}
operator[]
operator[] 功能强大,兼具查找、插入和修改功能。如果 Key 不存在,会自动插入默认构造的值;如果存在,则修改映射值。返回值为映射值的引用。
void test_4() {
map<string, string> dict;
dict["sort"] = "排序";
dict["string"] = "字符串";
auto it = dict["left"];
cout << it << endl;
}
常见场景是利用 operator[] 统计字符串中元素的出现次数:将元素作为 Key,计数作为 Value。
multimap 容器
multimap 允许插入相同 Key 的节点。除了 Key 可重复外,其他接口行为与 map 基本一致。
void Test01() {
multimap<string, string> mp;
mp.insert(make_pair("left", "左边"));
mp.insert(make_pair("left", "左边"));
mp.insert(make_pair("left", "左边"));
for (const auto& kv : mp) {
cout << kv.first << ": " << kv.second << endl;
}
}
void Test02() {
multimap<string, string> mp;
mp.insert(make_pair("left", "左边"));
mp.insert(make_pair("left", "左边"));
mp.insert(make_pair("left", "左边"));
mp.erase("left");
}
相关免费在线工具
- 加密/解密文本
使用加密算法(如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