- 翠微暑期集训
map学习
- @ 2026-10-7 15:52:12
C++ map 极简入门(初学者版)
提示:不喜勿喷
一、map 是什么?
你可以把 map 想象成一本字典:
- 字典里有"词"和"解释"。
- 给一个"词",就能查到它的"解释"。
在 C++ 里:
- "词"叫 键(key)
- "解释"叫 值(value)
map 就是用来存放一堆"键值对"的容器。它的特点是:键不能重复,而且会自动按 key 从小到大排序。
二、使用前准备
#include <iostream>
#include <map>
#include <string>
using namespace std;
map在<map>头文件里。- 如果键是字符串,需要
<string>。
声明一个 map:
map<string, int> m; // 键是 string,值是 int
比如:名字 → 分数。
三、最基本操作
1. 插入和修改
map<string, int> m;
m["apple"] = 3; // 插入:键 "apple",值 3
m["banana"] = 5; // 插入:键 "banana",值 5
m["apple"] = 10; // 修改:把 "apple" 的值改成 10
- 如果键不存在,
m["键"] = 值;会插入一个新元素。 - 如果键已存在,
m["键"] = 值;会覆盖原来的值。
2. 读取值
cout << m["apple"] << endl; // 输出 10
⚠️ 注意:如果 "apple" 不存在,m["apple"] 会自动插入一个默认值(int 默认是 0),然后返回 0。
3. 判断键是否存在
用 count:
if (m.count("apple") > 0) {
cout << "apple 存在" << endl;
} else {
cout << "apple 不存在" << endl;
}
count 返回 0 或 1(因为 map 的键不会重复)。返回 1 表示存在,0 表示不存在。
count 不会插入任何东西,也不会修改 map,是最安全的查询方式。
4. 删除元素
m.erase("apple"); // 删除键为 "apple" 的元素
5. 其他常用操作
m.size(); // 元素个数
m.empty(); // 是否为空,空返回 true
m.clear(); // 清空所有元素
四、专门说清楚:能不能用 m["apple"] == 0 判断"不存在"?
这是初学者最容易踩的坑,必须分开讲。结论不是"永远不行",而是要看你的值会不会是 0,以及你在不在乎 map 被悄悄插入元素。
情况一:可以用(但有副作用)
前提:你确定值永远不会是 0。
比如统计单词出现次数:
map<string, int> count;
count["apple"]++; // 计数只会是 1、2、3……不可能是 0
这种情况下,count["apple"] == 0 确实能表示"还没出现过",因为出现过的词次数一定大于 0。
但是! 即使这样写结果对,count["apple"] == 0 这个判断本身已经把 "apple" 插入进去了,只是值恰好是 0:
map<string, int> m;
if (m["apple"] == 0) { // 虽然判断成立,但 m 里已经多了一个 "apple"
cout << "不存在" << endl;
}
cout << m.size(); // 输出 1,而不是 0
如果你后面还要用 m.size(),或者要遍历,就会看到一个值为 0 的幽灵元素。
所以严格来说:只有在"值不会为 0,且你不在乎 map 被插入"时,这种写法才安全。
情况二:绝对不能用
前提:值本身可能是 0。
比如用 map 记录学生成绩,0 分是完全可能的:
map<string, int> score;
score["小明"] = 0; // 小明考了 0 分
if (score["小明"] == 0) {
cout << "小明不存在" << endl; // ❌ 错误!小明明明存在
}
这里你根本分不清"小明不存在"和"小明考了 0 分"。
再比如记录库存数量:
map<string, int> stock;
stock["苹果"] = 0; // 苹果卖完了,库存为 0
if (stock["苹果"] == 0) {
cout << "没有这种商品" << endl; // ❌ 错误!商品存在,只是库存为 0
}
只要值可能取到 0,就绝对不能用 == 0 判断存在性。
情况三:任何时候都安全的写法
不管你属于哪种情况,只要你想纯粹地"查询",都应该用 count:
if (m.count("apple") > 0) {
// apple 存在
} else {
// apple 不存在,而且 m 的大小没变
}
一句话总结
| 场景 | 能不能用 m["apple"] == 0 |
|---|---|
| 值永远不可能是 0,且你不在乎 map 被插入 | 可以,但要知道有副作用 |
| 值可能是 0(分数、库存、温度……) | 绝对不行 |
| 任何时候想安全地"只查询" | 用 m.count("apple") > 0 |
建议初学者养成习惯:判断存在性一律用 count,不要用 []。
五、遍历 map(按顺序输出所有键值对)
map 不能像数组那样用下标 m[0] 访问。需要用迭代器。
迭代器你可以理解为"一个指向 map 里某个元素的游标"。
固定写法:
map<string, int>::iterator it; // 声明一个迭代器
for (it = m.begin(); it != m.end(); it++) {
cout << it->first << " -> " << it->second << endl;
}
解释:
m.begin():指向第一个元素。m.end():指向最后一个元素的下一个位置(不是最后一个元素)。it != m.end():只要没到末尾就继续。it++:移动到下一个元素。it->first:当前元素的键。it->second:当前元素的值。
完整例子:
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
map<string, int> m;
m["apple"] = 3;
m["banana"] = 5;
m["cherry"] = 2;
map<string, int>::iterator it;
for (it = m.begin(); it != m.end(); it++) {
cout << it->first << " -> " << it->second << endl;
}
return 0;
}
输出:
apple -> 3
banana -> 5
cherry -> 2
你会发现:不管插入顺序如何,输出总是按 key 从小到大排列。 这就是 map 的自动排序。
六、完整小例子:统计单词出现次数
这是 map 最经典的练习。
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
map<string, int> count;
string word;
// 不断读入单词,直到输入结束(Windows 按 Ctrl+Z 再回车)
while (cin >> word) {
count[word]++; // 如果 word 不存在,先插入 0,再 +1
}
map<string, int>::iterator it;
for (it = count.begin(); it != count.end(); it++) {
cout << it->first << " 出现了 " << it->second << " 次" << endl;
}
return 0;
}
输入:
apple banana apple orange banana apple
输出:
apple 出现了 3 次
banana 出现了 2 次
orange 出现了 1 次
这里 count[word]++ 非常巧妙:
- 如果
word第一次出现,count[word]会自动变成0,然后++变成1。 - 如果已经出现过,就直接把原来的次数加
1。
注意:这里能用 count[word]++,正是因为"计数不可能是 0"这个特点。如果换成"学生成绩"这种值可能为 0 的场景,就不能随便用 [] 了。
七、初学者必须记住的坑
-
判断"存在性"不要用
[]if (m["apple"] == 0) { ... } // 危险!可能插入元素,且值本来就可能是 0正确做法:
if (m.count("apple") > 0) { ... }详情见第四节的三种情况。
-
m["键"] = 值;会覆盖旧值如果只想在不存在时插入,可以用
insert,但初学阶段先掌握[]就够了。 -
遍历时不要修改键
键是只读的,不能通过迭代器修改
it->first。 -
删除元素后,原来的迭代器可能失效
初学阶段先避免在遍历中删除,或者删除后重新开始遍历。
八、练习建议
- 输入几个整数,统计每个数出现了几次。
- 输入几个学生的名字和成绩,然后按名字从小到大输出。
- 输入一个字符串,统计每个字符出现的次数。
- 用 map 实现一个简单的电话簿:名字 → 电话号码,支持添加、查找、删除。
- 试一下:把"统计单词次数"里的
count["apple"]改成用count先判断,再输出,体会两种写法的区别。
九、总结
-
map存放键值对,键唯一,自动按 key 排序。 -
用
m["键"] = 值;插入或修改。 -
用
m["键"]读取值,但键不存在时会插入默认值。 -
判断存在性用
m.count("键") > 0。- 只有"值不可能为 0"且"不在乎被插入"时,才可以用
m["键"] == 0。
- 只有"值不可能为 0"且"不在乎被插入"时,才可以用
-
删除用
m.erase("键")。 -
遍历用迭代器:
map<string, int>::iterator it; for (it = m.begin(); it != m.end(); it++) { cout << it->first << " " << it->second << endl; } -
底层是红黑树,查找、插入、删除都很快(O(log n))。
把上面的单词统计例子亲手敲一遍,你就基本学会 map 了。