概述
unordered_map是 C++ STL 中的关联容器,基于哈希表(Hash Table) 实现。它提供键值对(Key-Value)的存储,支持平均 O(1) 时间复杂度的查找、插入和删除操作,是算法刷题和高效开发的重要工具。
核心 API
| 方法 | 功能描述 | 平均时间复杂度 | 最坏时间复杂度 |
|---|---|---|---|
operator[] | 访问或插入元素 | O(1) | O(n) |
insert() | 插入元素 | O(1) | O(n) |
erase() | 删除元素 | O(1) | O(n) |
find() | 查找元素(返回迭代器) | O(1) | O(n) |
count() | 统计键出现次数(0或1) | O(1) | O(n) |
size() | 获取元素个数 | O(1) | O(1) |
empty() | 检查是否为空 | O(1) | O(1) |
clear() | 清空容器 | O(n) | O(n) |
1. 基础语法与定义
基本定义
#include <unordered_map>
#include <string>
using namespace std;
// 定义一个键为string,值为int的unordered_map
unordered_map<string, int> umap;
注意事项
- 键必须支持哈希函数和相等比较
operator[]会自动插入不存在的键(值为默认构造)- 迭代器顺序是不确定的,与插入顺序无关
- 哈希冲突时性能会下降,最坏情况退化为O(n)
2. 常见写法与处理手段
基础操作:插入与访问
unordered_map<string, int> scores;
// 插入操作
scores.insert({"Alice", 95}); // 方法1
scores.emplace("Bob", 88); // 方法2(更高效)
scores["Charlie"] = 92; // 方法3
// 访问操作
cout << scores["Alice"]; // 输出95
// 访问不存在的键(会自动插入默认值)
cout << scores["David"]; // 输出0,并插入{"David", 0}
安全访问(避免自动插入)
unordered_map<string, int> scores;
// 错误写法:会插入默认值
// if(scores["Unknown"] > 0) { ... }
// 正确写法:使用find
auto it = scores.find("Unknown");
if(it != scores.end()) {
cout << it->second;
} else {
cout << "Key not found";
}
// 或者使用count
if(scores.count("Unknown") > 0) {
cout << scores["Unknown"];
}
遍历unordered_map
unordered_map<string, int> scores = {{"Alice", 95}, {"Bob", 88}};
// 方法1:使用迭代器
for(auto it = scores.begin(); it != scores.end(); ++it) {
cout << it->first << ": " << it->second << endl;
}
// 方法2:使用范围for循环(推荐)
for(const auto& pair : scores) {
cout << pair.first << ": " << pair.second << endl;
}
3. 常用模式与技巧
模式1:频率统计
vector<int> nums = {1, 2, 3, 2, 1, 3, 3};
unordered_map<int, int> freq;
// 统计频率
for(int num : nums) {
freq[num]++;
}
// 查找最大频率
int max_freq = 0;
for(const auto& [num, count] : freq) {
max_freq = max(max_freq, count);
}
模式2:两数之和/查找互补
vector<int> nums = {2, 7, 11, 15};
int target = 9;
unordered_map<int, int> seen; // 存储值和索引
for(int i = 0; i < nums.size(); i++) {
int complement = target - nums[i];
if(seen.find(complement) != seen.end()) {
cout << "Found: " << seen[complement] << " and " << i;
break;
}
seen[nums[i]] = i;
}
模式3:分组操作
vector<string> words = {"apple", "banana", "cat", "dog", "elephant"};
unordered_map<int, vector<string>> groups; // 按长度分组
for(const string& word : words) {
groups[word.length()].push_back(word);
}
// 输出所有长度为3的单词
for(const string& word : groups[3]) {
cout << word << " ";
}
5. 进阶技巧与注意事项
技巧1:避免重复计算哈希
// 在循环中缓存哈希值
unordered_map<string, int> cache;
string key = "some_long_key";
// 避免多次计算同一个键的哈希
auto it = cache.find(key);
if(it == cache.end()) {
// 只计算一次哈希
cache[key] = compute_expensive_value();
}
技巧3:批量操作
unordered_map<string, int> map1, map2;
// 合并两个map
map1.insert(map2.begin(), map2.end());
// 删除满足条件的元素
for(auto it = map1.begin(); it != map1.end(); ) {
if(it->second < 0) {
it = map1.erase(it);
} else {
++it;
}
}
6. 常见错误与调试
错误1:误用operator[]
unordered_map<string, int> scores;
// 错误:会插入默认值0
if(scores["unknown"] == 0) {
// 这里unknown键已经被插入了
}
// 正确:使用find或count
if(scores.find("unknown") != scores.end() && scores["unknown"] == 0) {
// ...
}
错误2:迭代器失效
unordered_map<string, int> scores = {{"a", 1}, {"b", 2}};
// 错误:在删除后继续使用迭代器
for(auto it = scores.begin(); it != scores.end(); ++it) {
if(it->first == "a") {
scores.erase(it); // 迭代器失效
// ++it; // 错误!
}
}
// 正确:使用erase返回值
for(auto it = scores.begin(); it != scores.end(); ) {
if(it->first == "a") {
it = scores.erase(it);
} else {
++it;
}
}
错误3:误解性能特征
// 错误:认为unordered_map总是O(1)
// 实际上哈希冲突时可能退化为O(n)
// 正确:了解数据特征,必要时调整哈希函数
// 或者使用map(保证O(log n)最坏情况)