一个"智能字典":给它一个键,立刻返回对应的值。内部用红黑树实现,元素按键自动排序,查找/插入/删除都是 O(log n)。
map 像一个"智能字典":你给它一个键(key),它立刻返回对应的值(value)。比如用名字查分数、用单词查出现次数。map 内部用红黑树实现,元素会按键的大小自动排序,查找、插入、删除都是 O(log n)。使用前需引入 <map> 头文件。
map 的键是唯一的,若插入已存在的键,会覆盖原有值。map 中的元素会按键的升序自动排序,遍历时总是按字典序或数值序输出。map 底层为红黑树,查找、插入、删除的时间复杂度均为 O(log n)。
| 1 | #include <iostream> |
| 2 | #include <map> // 必须包含这个头文件 |
| 3 | using namespace std; |
| 4 | |
| 5 | int 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 | } |
✏️ 插入与删除
| 1 | map<string, int> m; |
| 2 | m["Alice"] = 85; // 方式一:用 [] 赋值(键存在则覆盖) |
| 3 | m.insert({"Bob", 92}); // 方式二:用 insert(键已存在则不覆盖!) |
| 4 | m.erase("Alice"); // 删除键为 "Alice" 的元素 |
| 5 | m.clear(); // 清空所有元素 |
| 6 | m1.swap(m2); // 交换两个 map 的内容 |
insert 其实会返回一个 pair<iterator, bool>——bool 表示"这次是不是真的插入了一个新元素"(键已存在就是 false,插入成功是 true),iterator 则指向这个键(不管是新插入的还是本来就存在的)对应的位置。可以用这个返回值判断键是否已经存在,比如:if (m.insert({"Bob", 92}).second) cout << "插入成功";。日常写法里经常只是调用 insert(...) 而不关心返回值,但了解它的存在,遇到需要判断"插入是否成功"的场景时就用得上。🔍 查找与访问
| 1 | map<string, int> m = {{"Alice", 85}, {"Bob", 92}}; |
| 2 | |
| 3 | // 方式一:用 [](键不存在时会自动插入!) |
| 4 | int s1 = m["Alice"]; // 85——找到了 |
| 5 | int s2 = m["Eve"]; // 0——Eve 不存在,[] 会自动插入 {"Eve", 0}! |
| 6 | |
| 7 | // 方式二:用 at()(键不存在时抛出异常,更安全) |
| 8 | int s3 = m.at("Alice"); // 85——找到了 |
| 9 | // int s4 = m.at("Eve"); // 抛出异常——Eve 不存在 |
| 10 | |
| 11 | // 方式三:用 find()(返回迭代器,没找到返回 end()) |
| 12 | auto it = m.find("Bob"); |
| 13 | if (it != m.end()) { |
| 14 | cout << it->first << ": " << it->second << endl; // Bob: 92 |
| 15 | } |
| 16 | |
| 17 | // 方式四:用 count()(判断键是否存在,返回 0 或 1) |
| 18 | if (m.count("Alice")) { |
| 19 | cout << "Alice 存在" << endl; |
| 20 | } |
🔄 遍历
| 1 | map<string, int> m = {{"Bob", 92}, {"Alice", 85}, {"Carol", 78}}; |
| 2 | // 用迭代器遍历(按键的字典序自动排序) |
| 3 | for (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 更简洁 |
| 10 | for (auto& p : m) |
| 11 | { |
| 12 | cout << p.first << ": " << p.second << endl; |
| 13 | } |
陷阱一:[] 会自动插入不存在的键!
| 1 | map<string, int> m; |
| 2 | // 你只是想检查一下 "Alice" 的分数…… |
| 3 | cout << m["Alice"] << endl; // 输出:0 |
| 4 | // ❌ 但这一行悄悄地插入了 {"Alice", 0}! |
| 5 | cout << m.size() << endl; // 输出:1——map 里已经多了一个元素 |
| 6 | |
| 7 | // ✅ 安全做法:用 count() 或 find() 先检查 |
| 8 | if (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 和 [] 的行为不同
| 1 | map<string, int> m; |
| 2 | m["Eve"] = 95; // 插入 {"Eve", 95} |
| 3 | m["Eve"] = 100; // 覆盖!{"Eve", 100} |
| 4 | m.insert({"Eve", 80}); // 啥也没发生——"Eve" 已存在,insert 不会覆盖 |
| 5 | // m["Eve"] 仍然是 100 |
| 1 | #include <iostream> |
| 2 | #include <map> |
| 3 | using namespace std; |
| 4 | |
| 5 | int 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 | } |
把本节出现过的方法按用途归类汇总,写代码时可以直接当参考卡用。
| 方法 | 分类 | 作用 |
|---|---|---|
| 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() | 遍历 | 正向迭代器范围,按键升序 |
unordered_map 更合适。