以下是 C++ 中 std::map
的详细讲解,涵盖其核心特性、底层实现、操作方法和实际应用场景:
1. 基本概念
定义:
std::map
是有序关联容器,存储键值对(Key-Value Pairs),按键的升序自动排序。头文件:
#include <map>
核心特性:
键唯一性:每个键(Key)在容器中唯一。
基于红黑树:底层实现为平衡二叉搜索树(Red-Black Tree),保证操作效率。
排序特性:元素按键的严格弱序(默认
<
)排序,支持范围查询。时间复杂度:插入、删除、查找操作均为 O(log n)。
2. 初始化与构造
(1) 空容器
std::map<int, std::string> m1;
(2) 列表初始化(C++11+)
std::map<int, std::string> m2 = { {1, "Alice"}, {2, "Bob"}, {3, "Charlie"} };
(3) 自定义排序规则
// 按键的降序排序 struct Compare { bool operator()(int a, int b) const { return a > b; } }; std::map<int, std::string, Compare> m3;
3. 元素访问
(1) 通过键访问值
m2[2] = "Bob"; // 插入或修改键为2的值 std::string name = m2.at(1); // 访问键1的值(越界抛出异常)
(2) 迭代器遍历
for (auto it = m2.begin(); it != m2.end(); ++it) { std::cout << "Key: " << it->first << ", Value: " << it->second << std::endl; } // 范围for循环(C++11+) for (const auto& pair : m2) { std::cout << pair.first << " => " << pair.second << std::endl; }
4. 插入与删除
(1) 插入元素
m2.insert({4, "Dave"}); // 插入键值对 m2.emplace(5, "Eve"); // 直接构造元素(避免拷贝) m2.insert_or_assign(3, "Charles"); // 存在则修改,不存在则插入(C++17+)
(2) 删除元素
m2.erase(2); // 删除键为2的元素 auto it = m2.find(3); if (it != m2.end()) { m2.erase(it); // 通过迭代器删除 } m2.clear(); // 清空容器
5. 查找与判断
(1) 查找元素
auto it = m2.find(3); // 返回迭代器,未找到返回 end() if (it != m2.end()) { std::cout << "Found: " << it->second << std::endl; }
(2) 存在性检查
bool hasKey = m2.contains(4); // C++20 语法 // C++20 之前: hasKey = (m2.count(4) > 0);
(3) 范围查询
auto lower = m2.lower_bound(2); // 第一个 >=2 的键 auto upper = m2.upper_bound(4); // 第一个 >4 的键 for (auto it = lower; it != upper; ++it) { // 处理键在 [2, 4] 范围内的元素 }
6. 底层实现(红黑树)
红黑树特性:
每个节点包含颜色(红/黑)、键、值、左右子节点指针。
通过颜色规则和旋转操作保持树平衡,确保最长路径不超过最短路径的两倍。
插入/删除操作通过调整树结构维持平衡,保证 O(log n) 时间复杂度。
内存布局:
[Root (Black)] / \ [Red] [Red] / \ / \
7. 性能与复杂度
操作 | 时间复杂度 | 说明 |
---|---|---|
insert / emplace | O(log n) | 插入并维护树平衡 |
erase | O(log n) | 删除并维护树平衡 |
find / count | O(log n) | 基于键的二分查找 |
begin / end | O(1) | 获取首尾迭代器 |
8. 示例代码
#include <map> #include <iostream> int main() { std::map<std::string, int> scores = { {"Alice", 90}, {"Bob", 85}, {"Charlie", 95} }; // 插入新元素 scores.emplace("Dave", 88); scores["Eve"] = 92; // 插入或修改 // 删除元素 scores.erase("Bob"); // 查找并输出 auto it = scores.find("Alice"); if (it != scores.end()) { std::cout << "Alice's score: " << it->second << std::endl; } // 遍历输出 for (const auto& [name, score] : scores) { // C++17 结构化绑定 std::cout << name << ": " << score << std::endl; } return 0; }
9. 注意事项
键的类型要求:
必须支持严格弱序比较(默认使用
<
运算符)。若键为自定义类型,需提供比较函数(仿函数或重载
<
)。
迭代器失效:
删除元素会使指向被删元素的迭代器失效,其他迭代器不受影响。
与
unordered_map
对比:
map
有序,适合范围查询;unordered_map
无序但平均 O(1) 访问。
10. 适用场景
需要有序键:如按时间戳存储事件。
范围查询:如查找成绩在 [80, 90] 的学生。
键唯一性要求:如用户 ID 到用户信息的映射。
高频查找/低频插入:利用红黑树的稳定性能。
总结
std::map
是 C++ 中实现键值有序存储的核心工具,适合需要排序和范围查询的场景。尽管其时间复杂度为 O(log n),但在数据规模较大时,性能可能低于 std::unordered_map
。根据需求选择:
有序性 →
map
极速查找 →
unordered_map
系统当前共有 438 篇文章