← 目录 / 第十一章 · STL 标准模板库 / 11.8 map

11.8 map 有序映射

一个"智能字典":给它一个键,立刻返回对应的值。内部用红黑树实现,元素按键自动排序,查找/插入/删除都是 O(log n)。

本页目录

map 像一个"智能字典":你给它一个键(key),它立刻返回对应的值(value)。比如用名字查分数、用单词查出现次数。map 内部用红黑树实现,元素会按键的大小自动排序,查找、插入、删除都是 O(log n)。使用前需引入 <map> 头文件。

map<string, int>:键 → 值,并且自动按键的字典序排好
"Alice"
85
"Bob"
92
"Carol"
78
即使插入顺序是 Bob → Alice → Carol,遍历时也总是按键的字典序输出:Alice → Bob → Carol。
⚠️
注意:
· map 的键是唯一的,若插入已存在的键,会覆盖原有值。
· map 中的元素会按键的升序自动排序,遍历时总是按字典序或数值序输出。
· map 底层为红黑树,查找、插入、删除的时间复杂度均为 O(log n)。
11.8.1 定义与初始化
C++ · map 的定义方式
1#include <iostream>
2#include <map> // 必须包含这个头文件
3using namespace std;
4
5int main()
6{
7 map<string, int> m1; // 空映射:名字 → 分数
8 map<string, int> m2 = {
9 {"Alice", 85},
10 {"Bob", 92},
11 {"Carol", 78}
12 }; // 用花括号初始化
13 map<string, int> m3(m2); // 拷贝构造
14 map<int, string> m4; // 键是 int,值是 string
15 return 0;
16}
11.8.2 常用操作

✏️ 插入与删除

C++ · 插入、删除、清空
1map<string, int> m;
2m["Alice"] = 85; // 方式一:用 [] 赋值(键存在则覆盖)
3m.insert({"Bob", 92}); // 方式二:用 insert(键已存在则不覆盖!)
4m.erase("Alice"); // 删除键为 "Alice" 的元素
5m.clear(); // 清空所有元素
6m1.swap(m2); // 交换两个 map 的内容
↩️
insert 的返回值:insert 其实会返回一个 pair<iterator, bool>——bool 表示"这次是不是真的插入了一个新元素"(键已存在就是 false,插入成功是 true),iterator 则指向这个键(不管是新插入的还是本来就存在的)对应的位置。可以用这个返回值判断键是否已经存在,比如:if (m.insert({"Bob", 92}).second) cout << "插入成功";。日常写法里经常只是调用 insert(...) 而不关心返回值,但了解它的存在,遇到需要判断"插入是否成功"的场景时就用得上。

🔍 查找与访问

C++ · []、at()、find()、count() 四种查找方式
1map<string, int> m = {{"Alice", 85}, {"Bob", 92}};
2
3// 方式一:用 [](键不存在时会自动插入!)
4int s1 = m["Alice"]; // 85——找到了
5int s2 = m["Eve"]; // 0——Eve 不存在,[] 会自动插入 {"Eve", 0}!
6
7// 方式二:用 at()(键不存在时抛出异常,更安全)
8int s3 = m.at("Alice"); // 85——找到了
9// int s4 = m.at("Eve"); // 抛出异常——Eve 不存在
10
11// 方式三:用 find()(返回迭代器,没找到返回 end())
12auto it = m.find("Bob");
13if (it != m.end()) {
14 cout << it->first << ": " << it->second << endl; // Bob: 92
15}
16
17// 方式四:用 count()(判断键是否存在,返回 0 或 1)
18if (m.count("Alice")) {
19 cout << "Alice 存在" << endl;
20}

🔄 遍历

C++ · 用迭代器和范围 for 遍历
1map<string, int> m = {{"Bob", 92}, {"Alice", 85}, {"Carol", 78}};
2// 用迭代器遍历(按键的字典序自动排序)
3for (auto it = m.begin(); it != m.end(); ++it)
4{
5 cout << it->first << ": " << it->second << endl;
6}
7// 输出顺序:Alice: 85 Bob: 92 Carol: 78(按键排序)
8
9// 范围 for 更简洁
10for (auto& p : m)
11{
12 cout << p.first << ": " << p.second << endl;
13}
11.8.3 🔥 经典陷阱

陷阱一:[] 会自动插入不存在的键!

C++ · [] 的隐藏副作用
1map<string, int> m;
2// 你只是想检查一下 "Alice" 的分数……
3cout << m["Alice"] << endl; // 输出:0
4// ❌ 但这一行悄悄地插入了 {"Alice", 0}!
5cout << m.size() << endl; // 输出:1——map 里已经多了一个元素
6
7// ✅ 安全做法:用 count() 或 find() 先检查
8if (m.count("Alice"))
9{
10 cout << m["Alice"];
11}
🔥
记住:[] 会强制覆盖(或自动插入默认值 0),insert 不会覆盖已存在的键。如果只是想检查 key 是否存在,用 count()find()if (m.count("Alice")) cout << m["Alice"]; 像上面例子里那样,如果 map 中原本没有 "Alice",仅仅是"看一眼" m["Alice"] 这行代码,就会意外地往 map 里增加一个键值对——如果后面代码依赖"这个键存不存在"或者"map 里到底有多少个元素"来做判断,就可能因为这个意外插入而产生难以察觉的逻辑错误。

陷阱二:insert 和 [] 的行为不同

C++ · insert 不会覆盖已存在的键
1map<string, int> m;
2m["Eve"] = 95; // 插入 {"Eve", 95}
3m["Eve"] = 100; // 覆盖!{"Eve", 100}
4m.insert({"Eve", 80}); // 啥也没发生——"Eve" 已存在,insert 不会覆盖
5// m["Eve"] 仍然是 100
11.8.4 完整使用示例
C++ · map 综合示例
1#include <iostream>
2#include <map>
3using namespace std;
4
5int main()
6{
7 map<string, int> scores;
8
9 // 插入
10 scores["Alice"] = 85;
11 scores["Bob"] = 92;
12 scores["Carol"] = 78;
13 scores.insert({"David", 88});
14
15 // 覆盖(Alice 的成绩被更新)
16 scores["Alice"] = 90; // [] 会覆盖已有键的值;若改用 insert({"Alice", 90}),则不会更新已存在的 Alice
17
18 // 查找
19 if (scores.count("Bob")) {
20 cout << "Bob: " << scores["Bob"] << endl; // 输出:Bob: 92
21 }
22
23 // 遍历(按键的字典序自动排序)
24 cout << "全部成绩:" << endl;
25 for (auto& p : scores) {
26 cout << p.first << ": " << p.second << endl;
27 }
28 // 输出:Alice: 90 Bob: 92 Carol: 78 David: 88
29
30 // 删除
31 scores.erase("Carol");
32 cout << "删除后大小: " << scores.size() << endl; // 输出:3
33
34 return 0;
35}
📋 map 常用操作速查表

把本节出现过的方法按用途归类汇总,写代码时可以直接当参考卡用。

方法分类作用
m[key] = val插入/修改赋值,键存在则覆盖,不存在则插入
m.insert({key, val})插入插入,键已存在则不覆盖
m.erase(key)删除删除指定键的元素
m[key]查找键不存在时自动插入默认值,要小心
m.at(key)查找键不存在时抛出异常,更安全
m.find(key)查找返回迭代器,没找到返回 end()
m.count(key)查找判断键是否存在,返回 0 或 1
m.size() / m.empty()容量元素个数 / 判断是否为空
m.clear()容量清空所有元素
m.begin() / m.end()遍历正向迭代器范围,按键升序
🎯
什么时候用 map?需要按键查值、且希望遍历时键是有序的场景:统计单词出现次数并按字典序输出、按学号查学生信息、需要范围查询(比如找所有分数在某区间的人)等。如果不需要顺序、只追求最快查找速度,下一节的 unordered_map 更合适。