当前位置: 首页 > news >正文

std::unorderd_map 简介

1. unorderd_map 简介

  • 1. unorderd_map 简介
    • 简介
    • 1.1. 实现原理
    • 1.2. 函数
    • 1.3. 问题集
      • 1.3.1. emplace、emplace_hint、insert 的区别
    • 1.4. 参考链接

简介

  • unordered_map 是 C++ 标准库中的一个容器,它定义在 <unordered_map> 头文件里。它借助哈希表来存储键值对,能快速查找、插入和删除元素
  • 快速查找:平均情况下,查找、插入和删除操作的时间复杂度为 O ( 1 ) O(1) O(1)。不过在最坏情况下,例如哈希冲突严重时,时间复杂度会达到 O ( n ) O(n) O(n)
  • 无序存储:unordered_map 不会按照键的顺序存储元素,元素的存储顺序由哈希函数决定。
  • 键的唯一性:每个键在 unordered_map 中是唯一的。若尝试插入已存在的键,新的值会覆盖旧的值。

1.1. 实现原理

  • 底层实现主要基于哈希表(Hash Table)
  1. 哈希函数
  • unordered_map 使用哈希函数将键转换为一个无符号整数,这个整数将作为数组的索引。
  • 对于基本数据类型(如 int、string 等),标准库已经提供了默认的哈希函数。
  • 但对于自定义类型,需要用户自己定义哈希函数。

例如,对于自定义类型 MyKey,可以这样定义哈希函数:

#include <functional>struct MyKey {int a;double b;// 重载相等运算符,用于比较键是否相等bool operator==(const MyKey& other) const {return a == other.a && b == other.b;}
};// 自定义哈希函数
struct MyKeyHash {std::size_t operator()(const MyKey& k) const {// 使用 std::hash 对不同成员进行哈希,然后组合auto h1 = std::hash<int>{}(k.a);auto h2 = std::hash<double>{}(k.b);return h1 ^ (h2 << 1);}
};
  1. 哈希桶(Bucket)
  • 哈希表通常由一个数组构成,数组中的每个元素称为一个哈希桶。
  • 每个哈希桶可以存储一个或多个键值对,当多个键通过哈希函数映射到同一个桶时,就会发生哈希冲突。
  1. 处理哈希冲突
  • 哈希冲突是指不同的键通过哈希函数映射到了同一个桶。
  • unordered_map 通常使用链地址法(Separate Chaining)来处理哈希冲突。
  • 在链地址法中,每个哈希桶实际上是一个链表(或其他容器,如红黑树),当发生哈希冲突时,新的键值对会被添加到对应的链表中。

以下是一个简单的示意图,哈希表数组:

+--------+--------+--------+
| Bucket0| Bucket1| Bucket2|
+--------+--------+--------+
| Node1  | Node3  | Node4  |
| Node2  |        |        |
+--------+--------+--------+

在这个示意图中,Bucket0 发生了哈希冲突,有两个键值对(Node1 和 Node2)被映射到了这个桶中,它们通过链表连接在一起。

  1. 插入操作
  • 向 unordered_map 中插入一个键值对时,会执行以下步骤:
  • 使用哈希函数计算键的哈希值。
  • 根据哈希值找到对应的哈希桶。
  • 检查该桶中是否已经存在相同的键,如果存在,则更新对应的值;如果不存在,则将新的键值对插入到桶中(通常是链表的头部)。
  1. 查找操作
  • 当查找一个键对应的值时,会执行以下步骤:
  • 使用哈希函数计算键的哈希值。
  • 根据哈希值找到对应的哈希桶。
  • 遍历该桶中的链表(或其他容器),查找是否存在与给定键相等的键。如果找到,则返回对应的值;如果未找到,则返回一个表示未找到的标记(如 end() 迭代器)。
  1. 删除操作
  • 删除操作与查找操作类似,首先找到对应的哈希桶,然后遍历桶中的链表,找到要删除的键值对并将其从链表中移除。
  1. 负载因子(Load Factor)和扩容
  • 负载因子是指哈希表中元素的数量与哈希桶数量的比值。
  • 当负载因子超过某个阈值(通常是 1.0)时,为了减少哈希冲突,提高查找效率,unordered_map 会进行扩容操作。
  • 扩容时,会创建一个更大的哈希表数组,然后将原有的键值对重新哈希到新的数组中。

总结:unordered_map 通过哈希表实现了快速的键值对查找、插入和删除操作。它使用哈希函数将键映射到哈希桶,通过链地址法处理哈希冲突,并在负载因子过高时进行扩容。这种实现方式使得 unordered_map 在平均情况下具有 O(1) 的时间复杂度。

1.2. 函数

  • 成员方法

  • 迭代器

    • begin 返回指向容器中第一个键值对的正向迭代器。
    • end 返回指向容器中最后一个键值对之后位置的正向迭代器。
    • cbegin 和 begin 功能相同,只不过在其基础上增加了 const 属性,即该方法返回的迭代器不能用于修改容器内存储的键值对。
    • cend 和 end 功能相同,只不过在其基础上,增加了 const 属性,即该方法返回的迭代器不能用于修改容器内存储的键值对。
  • CRUD操作

    • operator[key] 该模板类中重载了 [] 运算符,只要给定某个键值对的键 key,就可以获取该键对应的值。注意,如果当前容器中没有以 key 为键的键值对,则其会使用该键向当前容器中插入一个新键值对。

    • at(key) 返回容器中存储的键 key 对应的值,如果 key 不存在,则会抛出 out_of_range 异常。

    • find(key) 查找以 key 为键的键值对,如果找到,则返回一个指向该键值对的正向迭代器;反之,则返回一个指向容器中最后一个键值对之后位置的迭代器(end 方法返回的迭代器)。

    • count(key) 在容器中查找以 key 键的键值对的个数。

    • equal_range(key) 返回一个 pair 对象,其包含 2 个迭代器,用于表明当前容器中键为 key 的键值对所在的范围。

    • emplace 向容器中添加新键值对,直接在容器内部构造元素,避免了不必要的拷贝或移动,效率比 insert 方法高。

    • emplace_hint 向容器中添加新键值对,接受一个迭代器 position 作为提示参数,用于提高插入效率,但提示错误可能会降低效率。

    • insert 向容器中添加新键值对,通常需要先构造好 value_type 对象,然后将其插入容器,可能会涉及额外的拷贝或移动操作。

    • erase 删除指定键值对。可以通过键、迭代器位置或迭代器范围来指定要删除的元素。

    • clear 清空容器,即删除容器中存储的所有键值对。

  • 属性

    • empty 若容器为空,则返回 true;否则 false。
    • size 返回当前容器中存有键值对的个数。
    • max_size 返回容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。
  • 类函数

    • swap 交换 2 个 unordered_map 容器存储的键值对,前提是必须保证这 2 个容器的类型完全相等。
  • 底层 hash 相关函数

    • bucket_count 返回当前容器底层存储键值对时,使用桶(一个线性链表代表一个桶)的数量。
    • max_bucket_count 返回当前系统中,unordered_map 容器底层最多可以使用多少桶。
    • bucket_size(n) 返回第 n 个桶中存储键值对的数量。
    • bucket(key) 返回以 key 为键的键值对所在桶的编号。
    • load_factor 返回 unordered_map 容器中当前的负载因子。负载因子,指的是的当前容器中存储键值对的数量(size)和使用桶数(bucket_count)的比值,即 load_factor = size / bucket_count。
    • max_load_factor 返回或者设置当前 unordered_map 容器的负载因子。
    • rehash(n) 将当前容器底层使用桶的数量设置为 n。
    • reserve 将存储桶的数量(也就是 bucket_count 方法的返回值)设置为至少容纳count个元(不超过最大负载因子)所需的数量,并重新整理容器。
    • hash_function 返回当前容器使用的哈希函数对象。

1.3. 问题集

1.3.1. emplace、emplace_hint、insert 的区别

  • 构造方式:
    • insert:通常需要先构造好 value_type 对象,然后将其插入容器,可能会涉及额外的拷贝或移动操作。
    • emplace 和 emplace_hint:直接在容器内部构造元素,避免了不必要的拷贝或移动,效率更高。
  • 提示参数:
    • insert 和 emplace:不需要提示参数。
    • emplace_hint:接受一个迭代器作为提示参数,用于提高插入效率,但提示错误可能会降低效率。
  • 返回值:
    • insert 和 emplace:返回一个 std::pair<iterator, bool>,表示插入是否成功。
    • emplace_hint:返回一个指向新插入元素或已存在元素的迭代器。
#include <iostream>
#include <unordered_map>int main() {std::unordered_map<int, std::string> myMap;auto result = myMap.insert({1, "one"});if (result.second) {std::cout << "Inserted successfully." << std::endl;} else {std::cout << "Element already exists." << std::endl;}return 0;
}
int main() {std::unordered_map<int, std::string> myMap;auto result = myMap.emplace(2, "two");if (result.second) {std::cout << "Emplaced successfully." << std::endl;} else {std::cout << "Element already exists." << std::endl;}return 0;
}
int main() {std::unordered_map<int, std::string> myMap;auto hint = myMap.begin();auto it = myMap.emplace_hint(hint, 3, "three");std::cout << "Inserted key: " << it->first << ", value: " << it->second << std::endl;return 0;
}

1.4. 参考链接

  • C++ STL unordered_map容器用法详解

相关文章:

  • NestJS——使用TypeORM操作数据库、增删改查、关联查询、QueryBuilder
  • 黑马 redis面试篇笔记
  • ROS-真机向虚拟机器人映射
  • zip是 Python 中 `zip` 函数的一个用法
  • PageView 内嵌套 TabBarView 的滑动冲突
  • 【C++指南】位运算知识详解
  • 利用软件I2C驱动OLED,点亮、熄灭OLED屏幕以及获取当前OLED屏幕开启状态
  • 【蓝桥杯】水质检测
  • 基于大语言模型的AI智能体开发:构建具备工具使用能力的智能助手
  • 一行命令打开iOS模拟器
  • [C] 第6章 C51函数
  • Spring Boot单元测试实战指南:从零到高效测试
  • SEO(Search Engine Optimization,搜索引擎优化)相关知识点
  • Linux:库的制作与原理
  • 第二章:langchain文本向量化(embed)搭建与详细教程-openai接口方式(上)
  • Linux网络编程 深入Linux网络栈:原始套接字链路层实战解析
  • 多语言笔记系列:共享数据
  • 从零开始学Python游戏编程37-精灵4
  • C++中的next_permutation全排列函数
  • Java学习手册:TCP 协议基础
  • “谁羽争锋”全国新闻界羽毛球团体邀请赛在厦门开赛
  • 远程控制、窃密、挖矿!我国境内捕获“银狐”木马病毒变种
  • 何立峰出席跨境贸易便利化专项行动部署会并讲话
  • 封江晚开江早,东北地区主要江河上一冰封期冰层较常年偏薄
  • 世卫发布预防少女怀孕新指南,呼吁终止童婚、延长女孩受教育时间
  • 天问三号开放20千克质量资源,邀国际合作开展火星探测研究