一文带你看懂unordered_map(附例题)

✅博客主页:爆打维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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值