✅博客主页:爆打维c-CSDN博客 🐾
🔹分享自己学习AI/编程知识的过程 🐾
🔹我的GitHub代码仓库 https://github.com/lyy-0118

今天刷题的时候看到了一个很适合学习unordered_map哈希表增删查改操作的题目,在这里分享给大家
一、unordered_map讲解
1. std::unordered_map 是什么
std::unordered_map 是 C++ STL 中的哈希表容器,用于存储“键值对”:
unordered_map<Key, Value>
例如:
unordered_map<string, int> mp;
表示用 string 类型作为键,用 int 类型作为值。
可以理解为:
姓名 -> 分数
"Tom" -> 90
"Jack" -> 85
它最大的特点是:通过 key 快速找到 value。
2. 核心特点
unordered_map 的底层一般是哈希表。
它会先对 key 计算一个哈希值,然后根据哈希值找到对应位置。
常见操作的平均时间复杂度是:

但注意是平均 O(1),不是绝对 O(1),如果发生大量哈希冲突,最坏情况可能退化到 O(N)。
3. 和 map 的区别
map 和 unordered_map 都能存键值对,但底层不同。

例如:
map<int, int> mp;
unordered_map<int, int> ump;
如果插入:
3 1
1 1
2 1
map 遍历时顺序是:1 2 3
unordered_map 遍历时顺序不固定,可能是:2 1 3,也可能是其他顺序。
所以 需要排序、区间查询:用 map,只需要快速查找:用 unordered_map
4. 基本用法
头文件:
#include <unordered_map>
定义:
unordered_map<string, int> mp;
增删查改:
//插入或修改:
mp["Tom"] = 90;
mp["Jack"] = 85;
mp["Tom"] = 100; // 修改 Tom 的成绩
//查询:
cout << mp["Tom"] << endl;
//删除:
mp.erase("Tom");
//统计数量:
cout << mp.size() << endl;
//清空:
mp.clear();
//判断是否为空:
if (mp.empty()) {
cout << "empty\n";
}
5. [ ] 的重要细节
[] 很方便,但有一个很重要的特点:
如果 key 不存在,
mp[key]会自动插入这个 key,并给 value 一个默认值。
请看下面的例子:
#include <iostream>
#include <unordered_map>
#include <string>
using namespace std;
int main() {
unordered_map<string, int> mp;
//访问不存在的 key 会自动插入:
cout << mp["Alice"] << endl;
// 输出 0
// 因为 "Alice" 原本不存在,会被自动插入,默认值为 0
//此时 map 中已经有一个元素:
cout << mp.size() << endl;
// 输出 1
//错误写法:
//如果只是想判断 key 是否存在,不建议使用 []
if (mp["Bob"]) {
cout << "exist\n";
}
// 如果 "Bob" 不存在,它会被自动插入,默认值为 0
//此时 Bob 也被插入了:
cout << mp.size() << endl;
// 输出 2
//正确写法:
//使用 find 判断 key 是否存在
if (mp.find("Tom") != mp.end()) {
cout << "exist\n";
}
//Tom 不存在,不会被插入:
cout << mp.size() << endl;
// 输出 2
return 0;
}
6. find() 查询
find() 返回一个迭代器。
auto it = mp.find("Tom");
如果找到了:it != mp.end()
如果没找到:it == mp.end()
完整例子:
unordered_map<string, int> mp;
mp["Tom"] = 90;
auto it = mp.find("Tom");
if (it != mp.end()) {
cout << it->second << endl;
} else {
cout << "Not found\n";
}
其中:it->first 表示 key,it->second表示 value。
7. count() 查询
除了find() 也可以用 count() 判断 key 是否存在。
if (mp.count("Tom")) {
cout << "exist\n";
}
对于 unordered_map 来说,key 唯一,因此:mp.count(key),结果只可能是 0 或 1
count()适合只判断是否存在。find()适合判断后还要取出 value。
8. 插入方式
常用方式一:直接用 []
mp["Tom"] = 90;
如果不存在,就插入。如果存在,就修改。
常用方式二:insert
mp.insert({"Jack", 80});
注意:insert 如果 key 已经存在,不会修改原来的值。
例如:
unordered_map<string, int> mp;
mp.insert({"Tom", 90});
mp.insert({"Tom", 100});
cout << mp["Tom"] << endl;
//输出仍然是:90
//如果想强制修改,用:
mp["Tom"] = 100;
//或者 C++17 的:
mp.insert_or_assign("Tom", 100);
9. 删除操作
删除某个 key:
mp.erase("Tom");
erase(key) 会返回删除的元素个数。
因为 unordered_map 中 key 唯一,所以返回值是:
- 删除成功:1
- 没找到:0
例如:
if (mp.erase("Tom")) {
cout << "Deleted successfully\n";
} else {
cout << "Not found\n";
}
这在比赛题里非常常用。
10. 遍历方式
unordered_map<string, int> mp;
mp["Tom"] = 90;
mp["Jack"] = 85;
for (auto p : mp) {
cout << p.first << " " << p.second << endl;
}
也可以写引用,避免拷贝:
for (auto &p : mp) {
cout << p.first << " " << p.second << endl;
}
但注意:遍历顺序是无序的,不稳定的。不要依赖它的输出顺序。
11. 适用场景
unordered_map 特别适合解决下面这些问题:
- 统计出现次数
- 记录某个元素是否出现
- 字符串映射到数字
- ID 映射到信息
- 快速查找
- 去重
- 前缀和 + 哈希
- 两数之和
- 学籍管理系统
- 单词频率统计
12. 注意事项
第一,unordered_map 不排序。如果题目要求按 key 从小到大输出,用 map 更方便。
第二,不要用 [] 判断存在性。因为它会自动插入不存在的 key。
第三,大数据时建议预留空间:mp.reserve(200000);这样可以减少扩容次数,提高效率。
第四,分数或数据范围较大时,value 可以用 long long:unordered_map<string, long long> mp;
第五,如果 key 是自定义结构体,需要自己写哈希函数。普通类型如:int、long long、string、char都可以直接作为 key。
二、题目讲解
题目链接:https://www.luogu.com.cn/problem/P5266

1. 题目分析
题目要求实现一个简易的学籍管理系统,核心需求是处理四种操作:插入/更新成绩、查询成绩、删除记录以及统计总人数。题目操作量 Q 高达 10^5,如果使用普通数组进行遍历查找,单次操作最坏需要 O(N),总时间复杂度会退化到 O(N^2),必然会导致超时。因此,我们需要一种能够将“姓名(字符串)”和“成绩(数字)”绑定,且支持极速查找、插入和删除的数据结构。
2. 算法知识
这题是经典的哈希表(Hash Table) / 字典映射应用题。
在 C++ STL 中,处理键值对映射主要有两种容器:
- std::map:底层是红黑树,单次操作时间复杂度为 $O(\log N)$,键是有序的。
- std::unordered_map:底层是哈希表,单次操作平均时间复杂度为 $O(1)$,键是无序的。
因为题目不需要按姓名排序输出,只追求极致的精确查找速度,使用 std::unordered_map 是最优解。通过其内置的 insert/[ ] 进行存改,find() 进行查找,erase() 进行删除,size() 获取总量,即可完美 O(1) 解决此题。同时注意分数范围接近 2^{31},为防边界溢出,成绩建议使用 long long 存储。
3. 实现代码
#include
#include
#include
using namespace std;
int main() {
// 蓝桥杯常识:关闭同步流,大幅提升 cin/cout 的读写速度,防止大数据量超时
ios::sync_with_stdio(false);
cin.tie(nullptr);
int q;
if (!(cin >> q)) return 0;
// 定义哈希表:键为姓名(string),值为分数(long long)
unordered_map sys;
while (q--) {
int op;
cin >> op;
if (op == 1) {
string name;
long long score;
cin >> name >> score;
sys[name] = score; // 如果存在则覆盖更新,不存在则插入
cout > name;
auto it = sys.find(name); // 查找学生
if (it != sys.end()) {
cout second second 获取对应的成绩
} else {
cout > name;
// erase 方法在 unordered_map 中会返回成功删除的元素个数(0 或 1)
if (sys.erase(name)) {
cout << "Deleted successfully\n";
} else {
cout << "Not found\n";
}
} else if (op == 4) {
cout << sys.size() << "\n"; // 直接输出哈希表当前大小
}
}
return 0;
}
1万+

被折叠的 条评论
为什么被折叠?



