好的,我们来深入剖析STL中的 map 和 multimap 容器,重点解析它们的接口使用和核心特性。
1. 基本概念与核心差异
- map (映射):
- 关联容器,存储键值对 (key, value)。
- 核心特性: key 必须唯一。 即容器中任意两个元素的 key 不能相同。
- 内部元素根据 key 自动排序(默认升序,可通过比较函数自定义)。
- multimap (多重映射):
- 同样是关联容器,存储键值对 (key, value)。
- 核心特性: 允许 key 重复。 即容器中可以存在多个具有相同 key 的元素。
- 内部元素同样根据 key 自动排序。
核心差异总结:
- key 的唯一性: map 要求唯一,multimap 允许重复。
- 应用场景:
- map: 需要建立严格一对一映射关系的场景(如字典、电话簿、唯一ID映射)。
- multimap: 需要建立一对多映射关系的场景(如一个作者对应多本书、一个班级对应多个学生)。
2. 底层数据结构:红黑树
map 和 multimap 在标准库实现中通常基于红黑树(Red-Black Tree)。
- 红黑树特性:
- 自平衡的二叉搜索树。
- 插入、删除、查找操作的时间复杂度为 $O(\\log n)$。
- 保证了元素的有序性(根据 key 排序)。
- 影响:
- 高效的有序查找:find(), lower_bound(), upper_bound() 等操作高效。
- 插入和删除操作会调整树结构以保持平衡。
- 迭代器遍历时,元素按 key 的排序顺序输出(中序遍历)。
3. 关键接口详解
3.1 插入元素
- map 插入 (insert):
- std::pair<iterator, bool> insert(const value_type& value);
- 尝试插入一个键值对 (key, value)。
- 返回值是一个 pair:
- pair.first: 指向被插入元素的迭代器(或指向阻止插入的元素的迭代器)。
- pair.second: bool 值,表示插入是否成功。
- true: 插入成功(key 原本不存在)。
- false: 插入失败(key 已存在,已有元素未被替换)。
- 重要特性: 如果 key 已存在,插入操作不会覆盖原有的 value。
- map 下标访问 (operator[]):
- T& operator[](const Key& key);
- T& operator[](Key&& key); (C++11)
- 行为:
- 如果 key 存在,返回其对应 value 的引用。
- 如果 key 不存在,则插入一个新的键值对 (key, T())(值初始化),并返回新 value 的引用。
- 注意: 这可能导致意外的元素插入。如果只想查询而不想插入,应使用 find()。
- map 插入或赋值 (insert_or_assign) (C++17):
- std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj);
- 如果 key k 不存在,则插入 (k, std::forward<M>(obj))。
- 如果 key k 已存在,则将已有元素的 value 赋值为 std::forward<M>(obj)。
- 返回值 pair 中:
- iterator: 指向插入或修改元素的迭代器。
- bool: true 表示插入了新元素,false 表示赋值了已有元素。
- map/multimap 使用 emplace (C++11):
- template <class… Args> std::pair<iterator, bool> emplace(Args&&… args); (map)
- template <class… Args> iterator emplace(Args&&… args); (multimap)
- 在容器中直接构造元素(键值对),避免临时对象的构造和拷贝/移动。
- 对于 map,返回值 pair 的含义与 insert 相同。
- 对于 multimap,总是返回指向新插入元素的迭代器(因为 key 可重复,总能插入)。
- multimap 插入 (insert):
- iterator insert(const value_type& value);
- 插入一个键值对 (key, value)。
- 总是成功(允许 key 重复)。
- 返回指向新插入元素的迭代器。
3.2 查找元素
- find (查找特定 key):
- iterator find(const Key& key);
- const_iterator find(const Key& key) const;
- 查找具有特定 key 的元素。
- 返回指向找到的元素的迭代器。如果未找到,返回 end()。
- map: 最多找到一个元素(key 唯一)。
- multimap: 返回找到的第一个具有该 key 的元素(需配合 equal_range 或循环查找所有)。
- count (统计特定 key 的数量):
- size_type count(const Key& key) const;
- 返回具有特定 key 的元素数量。
- map: 结果只能是 0 或 1。
- multimap: 结果可以是 0, 1, 或大于 1。
- equal_range (查找特定 key 的范围):
- std::pair<iterator, iterator> equal_range(const Key& key);
- std::pair<const_iterator, const_iterator> equal_range(const Key& key) const;
- 返回一个 pair,包含两个迭代器:
- first: 指向第一个 key 不小于 key 的元素(即等于 key 或逻辑上第一个大于 key 的元素)。
- second: 指向第一个 key 大于 key 的元素。
- 对于特定 key k,[lower_bound(k), upper_bound(k)) 这个区间包含所有 key == k 的元素。
- map: 返回的范围最多包含一个元素(first 可能等于 second)。
- multimap: 返回的范围包含所有 key == k 的元素。
- lower_bound / upper_bound (查找边界):
- iterator lower_bound(const Key& key);
- iterator upper_bound(const Key& key);
- 含义与 equal_range 返回的 pair 中的 first 和 second 相同。
- 常用于在有序序列中查找一个区间。
3.3 删除元素
- erase (删除元素):
- iterator erase(iterator pos); (C++11 前返回 void)
- iterator erase(const_iterator pos); (C++11)
- size_type erase(const Key& key);
- iterator erase(const_iterator first, const_iterator last);
- 删除单个元素(通过迭代器)、删除所有具有特定 key 的元素、删除一个迭代器范围 [first, last)。
- 删除后返回的迭代器指向被删除元素的下一个元素(或 end())。
- 删除所有具有特定 key 的元素,返回被删除的元素个数。
- map: erase(const Key& key) 返回值是 0 或 1。
- multimap: erase(const Key& key) 返回值可能大于 1。
- 注意: 删除元素会使指向该元素的迭代器、引用、指针失效。指向其他元素的通常保持有效。
3.4 其他重要接口
- empty / size: 检查是否为空、获取元素数量。
- clear: 清空容器。
- 迭代器 (begin, end, cbegin, cend, rbegin, rend, crbegin, crend): 用于遍历容器。遍历时元素按键排序顺序输出。
- key_comp / value_comp: 获取用于比较 key 或 value(实际比较的是 key)的函数对象副本。
4. 性能特点
- 查找 (find, count, operator[]): $O(\\log n)$ (基于红黑树)
- 插入 (insert, emplace): $O(\\log n)$ (查找插入位置 + 可能的树平衡调整)
- 删除 (erase): $O(\\log n)$ (查找元素 + 可能的树平衡调整)
- 空间复杂度: $O(n)$
5. 使用示例
#include <iostream>
#include <map>
#include <string>
int main() {
// map 示例 (唯一键)
std::map<int, std::string> studentMap;
// 插入 – 使用 insert
auto [it1, success1] = studentMap.insert({101, "Alice"});
if (success1) std::cout << "Inserted Alice\\n";
auto [it2, success2] = studentMap.insert({101, "Bob"}); // key 101 已存在,插入失败
if (!success2) std::cout << "Failed to insert Bob for key 101\\n";
// 插入 – 使用 operator[]
studentMap[102] = "Charlie"; // 插入新元素 (102, "Charlie")
std::cout << "Student 102: " << studentMap[102] << '\\n';
studentMap[101] = "Alan"; // key 101 存在,修改 value 为 "Alan"
std::cout << "Student 101 now: " << studentMap[101] << '\\n';
// 插入 – 使用 emplace
studentMap.emplace(103, "Diana");
// 查找
auto it_find = studentMap.find(102);
if (it_find != studentMap.end()) {
std::cout << "Found 102: " << it_find->second << '\\n';
}
// 遍历 (按键排序: 101, 102, 103)
for (const auto& [id, name] : studentMap) {
std::cout << id << ": " << name << '\\n';
}
// multimap 示例 (允许重复键)
std::multimap<std::string, std::string> authorBooks;
authorBooks.insert({"J.K. Rowling", "Harry Potter 1"});
authorBooks.insert({"J.K. Rowling", "Harry Potter 2"});
authorBooks.insert({"George Orwell", "1984"});
authorBooks.insert({"George Orwell", "Animal Farm"});
// 查找一个作者的所有书 – 使用 equal_range
auto range = authorBooks.equal_range("J.K. Rowling");
std::cout << "\\nBooks by J.K. Rowling:\\n";
for (auto it = range.first; it != range.second; ++it) {
std::cout << " – " << it->second << '\\n';
}
return 0;
}
https://weibo.com/tv/show/1034:5276907978293255
https://weibo.com/tv/show/1034:5276907961516059
https://weibo.com/tv/show/1034:5276907948933124
https://weibo.com/tv/show/1034:5276907936088108
https://weibo.com/tv/show/1034:5276907910922284
https://weibo.com/tv/show/1034:5276907906990117
https://weibo.com/tv/show/1034:5276907881824266
https://weibo.com/tv/show/1034:5276907881824272
https://weibo.com/tv/show/1034:5276907839881270
https://weibo.com/tv/show/1034:5276907839619097
6. 总结
- map 提供唯一的键到值的映射,操作高效 ($O(\\log n)$),元素有序存储。
- multimap 允许键重复,适用于一对多的映射关系,接口与 map 类似但插入和删除行为有差异(允许多个相同 key)。
- 两者均基于红黑树实现,保证了操作的效率和元素的有序性。
- 理解 insert, operator[], emplace 的行为差异以及 find, count, equal_range 的用法至关重要。
- 在需要键唯一时用 map,在需要键可重复时用 multimap。
- https://weibo.com/tv/show/1034:5276907978293255
https://weibo.com/tv/show/1034:5276907961516059
https://weibo.com/tv/show/1034:5276907948933124
https://weibo.com/tv/show/1034:5276907936088108
https://weibo.com/tv/show/1034:5276907910922284
https://weibo.com/tv/show/1034:5276907906990117
https://weibo.com/tv/show/1034:5276907881824266
https://weibo.com/tv/show/1034:5276907881824272
https://weibo.com/tv/show/1034:5276907839881270
https://weibo.com/tv/show/1034:5276907839619097




