map | unordered_map | set | unordered_set是四类重要的关联容器,它们是构建高效数据结构和算法的基石,常用于通过键 (key) 来快速查找、插入和删除数据。
一、认识四大关联容器
| 有序 (基于红黑树, O(log N)) | 无序 (基于哈希表, O(1)平均) | |
|---|---|---|
| 键值对 (字典) | std::map | std::unordered_map |
| 仅键 (集合) | std::set | std::unordered_set |
1. std::map - 自动排序的字典
- 比喻:一本永远按首字母排好序的字典。
- 头文件:
#include <map> - 核心:存储唯一的、根据键自动升序排序的键值对。
2. std::set - 自动排序的唯一集合
- 比喻:一份不允许重名且自动按字母顺序排好队的嘉宾名单。
- 头文件:
#include <set> - 核心:存储唯一的、自动升序排序的元素。
3. std::unordered_map - 高性能的杂乱字典
- 比喻:一本查找速度极快,但内容杂乱无章的字典。
- 头文件:
#include <unordered_map> - 核心:存储唯一的、无序的键值对,平均查找速度最快。
4. std::unordered_set - 高性能的杂乱集合
- 比喻:一份登记速度极快,但名单顺序杂乱的嘉宾列表。
- 头文件:
#include <unordered_set> - 核心:存储唯一的、无序的元素,平均查找速度最快。
二、增删查改遍历
对于C++初学者,不必学习每个函数的细枝,深究其内部实现,学会增删查遍历改足以。
对于 std::map 和 std::unordered_map:
- 插入/修改: 熟练使用
my_map[key] = value;。 - 查找: 必须掌握
find()!if (my_map.find(key) != my_map.end())是判断键是否存在的标准做法。 - 删除: 熟练使用
my_map.erase(key);。 - 遍历: 熟练使用
for (const auto& pair : my_map),并知道pair.first是键,pair.second是值。
对于 std::set 和 std::unordered_set:
- 插入: 熟练使用
my_set.insert(value);。 - 查找: 熟练使用
count()判断存在性,if (my_set.count(value))更简洁。 - 删除: 熟练使用
my_set.erase(value);。 - 遍历: 熟练使用
for (const auto& value : my_set)。
三、组合容器
学习容器的最终目的是解决实际问题,而这通常需要将它们组合起来。
核心用法:map 存储 vector
map 的键用来索引,vector 作为值用来存储一组数据。
案例:记录每个学生的多门考试成绩
#include <iostream>
#include <string>
#include <vector>
#include <map>
int main() {
// 键是学生姓名 (string),值是该学生的所有成绩 (vector<int>)
std::map<std::string, std::vector<int>> student_grades;
// --- 为张三添加成绩 ---
// 1. 直接用 [] 访问 "Zhang San"。因为他不存在,map 会自动创建一个键 "Zhang San"
// 和一个与之关联的空 vector。
// 2. 然后对这个 vector 调用 push_back() 添加成绩。
student_grades["Zhang San"].push_back(95);
student_grades["Zhang San"].push_back(88);
student_grades["Zhang San"].push_back(92);
// --- 为李四添加成绩 ---
student_grades["Li Si"].push_back(100);
student_grades["Li Si"].push_back(76);
// --- 遍历并打印所有学生的成绩 ---
std::cout << "所有学生的成绩单:" << std::endl;
for (const auto& pair : student_grades) {
std::cout << "学生: " << pair.first << " 成绩: ";
// pair.second 就是那个 vector
for (int grade : pair.second) {
std::cout << grade << " ";
}
std::cout << std::endl;
}
}
输出:
所有学生的成绩单:
学生: Li Si 成绩: 100 76
学生: Zhang San 成绩: 95 88 92
核心技巧:my_map[key] 会返回与 key 关联的那个 vector 的引用。所以可以直接在其后调用 vector 的成员函数,如 .push_back(),代码非常简洁。
初学者陷阱:set 存储 vector
是否可以 std::set<std::vector<int>> 来存储一组不重复的 vector 呢?
答案是:理论上可以,但实践中很复杂,初学者应避免。
set 需要对内部元素进行排序,但 C++ 默认不知道如何比较两个 vector 的“大小”。直接使用会导致编译错误。虽然可以通过自定义比较器解决,但这是一个高级话题。
建议:set 和 unordered_set 主要用来存储 int, double, std::string 等基础类型。不要轻易尝试将 vector 或 map 等复杂容器作为它们的元素。
总结与决策指南
-
需要存键值对吗?
- 是 ->
map或unordered_map。 - 否 ->
set或unordered_set。
- 是 ->
-
需要数据根据键自动排序吗?
- 是 ->
map或set。 - 否,只想要最快的查找速度 ->
unordered_map或unordered_set。
- 是 ->
最重要的经验法则:如果不确定,并且不强制要求排序,请优先选择 unordered 版本,因为它们通常更快,性能更好。
STL关联容器基础用法解析
1490

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



