【C++ —— 认识哈希和unordered_set、unordered_map的介绍及模拟】

认识哈希和unordered_set、unordered_map的介绍及模拟

  • 哈希表基础
    • 哈希的概念
    • 哈希表的基本操作
  • 哈希冲突
    • 哈希冲突的定义
    • 哈希冲突的影响
    • 常见的哈希冲突的解决方法
  • 哈希函数
    • 哈希函数的定义
    • 哈希函数的设计原则
    • 常见的哈希函数
  • unordered系列关联式容器
  • hash模拟

哈希表基础

哈希的概念

哈希表(Hash Table),也称为散列表,是一种通过哈希函数将键值(Key) 映射到对应位置(Bucket)数据结构。哈希表使用一个数组来存储数据,通过哈希函数计算出键值的索引,插入、查找和删除操作的时间复杂度可以接近 O ( 1 ) O(1) O(1)

unordered系列的关联式容器之所以效率比较高,是因为其底层使用了哈希结构。

哈希表的基本操作

当向哈希表的结构中:

  • 插入元素
    根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放。
  • 搜索元素
    对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置
    取元素比较,若关键码相等,则搜索成功。

该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(HashTable)(或者称散列表)


例如:数据集合{1,7,6,4,5,9};
哈希函数设置为:hash(key) = key % capacity; capacity为存储元素底层空间总的大小。

在这里插入图片描述
用该方法进行搜索不必进行多次关键码的比较,因此搜索的速度比较快
问题:按照上述哈希方式,向集合中插入元素44,会出现什么问题?

哈希冲突

哈希冲突的定义

哈希冲突(Hash Collision) 是指两个或多个不同的键通过哈希函数映射到同一个位置。当哈希表发生冲突时,需要使用某种机制来解决冲突,确保所有键值对能够正确存储和查找。

哈希冲突的影响

哈希冲突会导致数据存储位置重叠,增加查找时间,降低哈希表的整体性能。频繁的冲突会使哈希表的操作复杂度从 O ( 1 ) O(1) O(1)恶化为 O ( n ) O(n) O(n),影响系统性能。

常见的哈希冲突的解决方法

1. 开放地址法
开放地址法通过探测空闲位置来解决冲突。常见的探测方法有:

下面讲重点讲解线性探测二次探测分离链接法

线性探测(Linear Probing)

  • 发生冲突时,从当前位置开始,逐个检查后续位置,直到找到空位为止。
  • 优点:实现简单。
  • 缺点:可能导致“堆积”现象,即冲突连续发生。

比如上述中的场景,现在需要插入元素44,先通过哈希函数计算哈希地址,hashAddr为4,
因此44理论上应该插在该位置,但是该位置已经放了值为4的元素,即发生哈希冲突。

线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。

  • 插入

    • 通过哈希函数获取待插入元素在哈希表中的位置
    • 如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突,使用线性探测找到下一个空位置,插入新元素
      在这里插入图片描述
  • 删除

    • 采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索素。 比如删除元素4,如果直接删除掉,44查找起来可能会受影响。因此线性探测采用标记的伪删除法来删除一个元素。

二次探测(Quadratic Probing)

  • 发生冲突时,从当前位置开始,以二次方为步长逐个检查后续位置(例如,1^2, 2^2, 3^2,…),直到找到空位为止。
  • 优点:减少线性探测中的堆积现象。
  • 缺点:需要处理哈希表大小和步长选择的问题。

二次探测

 线性探测的缺陷是产生冲突的数据堆积在一块,这与其找下一个空位置有关系,因为找空位置的方式就是挨着往后逐个去找,因此二次探测为了避免该问题,找下一个空位置的方法为: H i H_i Hi =( H 0 H_0 H0 + i 2 i^2 i2 )% m, 或者: H i H_i Hi = ( H O H_O HO - i 2 i^2 i2 )% m。其中: i =1,2,3., H o H_o Ho是通过散列函数Hash(x)对元素的关键码key进行计算得到的位置,m是表的大小。对于上述中如果要插入44,产生冲突,使用解决后的情况为:

在这里插入图片描述

  研究表明:当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能够插入,而且任何一个位置都不会被探查两次。因此只要表中有一半的空位置,就不会存在表满的问题。在搜素时可以不考虑表装满的情况,但在插入时必须确保表的装载因子a不超过0.5,如果超出必须考虑增容。


双重哈希(Double Hashing)

  • 发生冲突时,使用第二个哈希函数计算新的步长,从当前位置开始探测空闲位置。
  • 优点:进一步减少冲突聚集。
  • 缺点:需要设计两个有效的哈希函数。

2. 链地址法
链地址法通过在每个哈希表位置存储一个链表来解决冲突。

分离链接(Separate Chaining)

  • 分离链接法通过将每个哈希表槽位维护一个链表来处理冲突。
  • 每个哈希表槽位存储一个链表,所有冲突的元素都插入该链表中。
  • 优点:简单易实现,动态调整链表长度。
  • 缺点:在最坏情况下(所有元素都落在同一个桶中),查找时间复杂度退化为 O ( n ) O(n) O(n)

在这里插入图片描述
在这里插入图片描述

自适应链表(Adaptive Chaining)

  • 根据链表长度和冲突情况,动态调整存储结构,例如在链表长度较长时将其转换为红黑树。
  • 优点:在链表较短时保持简单性,在链表较长时提高查找效率。
  • 缺点:实现较为复杂,需要维护多种数据结构。

哈希函数

哈希函数的定义

哈希函数的定义
 哈希函数(Hash Function)是将键值转换为哈希值的函数。哈希值用于确定键值在哈希表中的存储位置。一个好的哈希函数能均匀分布键,减少冲突,提高哈希表的性能。

哈希函数的设计原则

  • 均匀分布:哈希值应均匀分布在整个哈希表,避免冲突集中在某些位置。
  • 快速计算:哈希函数应高效计算,减少额外开销,提高性能。
  • 最小冲突:尽量避免相同哈希值的情况,使不同的键尽量映射到不同的位置。

常见的哈希函数

1. 除留余数法 (Modulo Division Method)
 解释:除留余数法是最简单和常用的哈希函数之一。它通过将键除以哈希表的大小并取余数来生成哈希值。这样生成的哈希值会在0到表大小-1的范围内,适合直接作为哈希表的索引。

优点:
 实现简单,计算快速。
缺点:
 如果键的分布不均匀,可能导致冲突较多。
C++代码

int modHash(int key, int table_size) 
{return key % table_size;
}

2. 平方取中法 (Mid-Square Method)
 解释:平方取中法是将键值平方,然后取中间几位作为哈希值。这样做的优点是可以通过平方操作增加哈希值的复杂性,从而使得哈希值更均匀地分布。

优点:
 中间位数的选择可以减少键值的偏差,减少冲突。
缺点:
 适用于键值长度较短的情况。
C++代码:

int midSquareHash(int key, int table_size) 
{int squared = key * key;// 假设key是一个4位数,取中间2位int mid_digits = (squared / 100) % 100;return mid_digits % table_size;
}

3. 直接定址法 (Direct Addressing Method)
 解释:直接定址法将键值直接映射到哈希表的索引上,适用于键值范围较小且较为密集的情况。键值直接作为索引使用,不需要额外计算。

优点:
 查找、插入和删除操作的时间复杂度都是O(1)。
 实现非常简单。
缺点:
 需要保证键值的范围在哈希表大小之内,否则会导致索引越界。
 键值范围大且稀疏时会浪费内存。
C++代码:

int multiplicativeHash(int key, int table_size) 
{double A = 0.618033;  // (sqrt(5) - 1) / 2 的值double frac = key * A - int(key * A);return int(table_size * frac);
}

unordered系列关联式容器

unordered系列关联式容器 包含 unordered_mapunordered_set,在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到 l o g 2 N log_2 N log2N,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好的查询是,进行很少的比较次数就能够将元素找到,因此在C++11中,STL又提供了4个unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同。

unordered_map/unordered_set 和 map/set 的区别

特性map/setunordered_map/unordered_set
内部实现红黑树(自平衡二叉搜索树)哈希表
元素顺序有序(按键值排序)无序(按哈希值存储)
时间复杂度O(log n)平均 O(1),最坏 O(n)
迭代顺序按键值顺序无固定顺序
内存占用较高(存储节点信息)较低(可能有冲突链表开销)
使用场景需要有序存储和范围查询需要快速查找、插入和删除

简要总结

  • map 和 set:适用于需要保持元素有序的场景,例如有序字典、按键值范围查询。
  • unordered_map 和 unordered_set:适用于需要快速查找、插入和删除的场景,且对元素顺序没有要求。

unordered_map/unordered_set 的接口可以查询cplusplus.com使用用法与之前介绍的相似。

hash模拟

开放地址法hash

#pragma once
#include <vector>template<class K>
struct HashFunc
{size_t operator()(const K& key){return (size_t)key;}
};//特化
template<>
struct HashFunc<string>
{size_t operator()(const string& key){size_t hash = 0;for (auto ch : key){hash *= 131;hash += ch;}return hash;}
};namespace open_address
{enum State{EMPTY,EXIST,DELETE};template<class K, class V>struct HashData{pair<K, V> _kv;State _state = EMPTY;};struct StringHashFunc{size_t operator()(const string& k){size_t hash = 0;for (auto& e : k){hash *= 131;hash += e;}return hash;}};template<class K, class V, class Hash = HashFunc<K>>class HashTable{public:HashTable(){_tables.resize(10);}bool Insert(const pair<K, V>& kv){if (Find(kv.first))return false;//扩容if (_n * 10 / _tables.size() >= 7){HashTable<K, V, Hash> newHT;newHT._tables.reserve(_tables.size() * 2);//旧表插入到新表for (size_t i = 0; i < _tables.size(); i++){if (_tables[i]._state == EXIST){newHT.Insert(_tables[i]._kv);}}_tables.swap(newHT._tables);}Hash hs;size_t hashi = hs(kv.first) % _tables.size();//线性探测while (_tables[hashi]._state == EXIST){hashi++;hashi %= _tables.size();}_tables[hashi]._kv = kv;_tables[hashi]._state = EXIST;_n++;return true;}HashData<K, V>* Find(const K& k){Hash hs;size_t hashi = hs(k) % _tables.size();//线性探测while (_tables[hashi]._state != EMPTY){if (_tables[hashi]._state == EXIST && _tables[hashi]._kv.first == k){return &_tables[hashi];}hashi++;hashi %= _tables.size();}return nullptr;}bool Erase(const K& k){HashData<K, V>* ret = Find(k);if (ret == nullptr){return false;}else{ret->_state = DELETE;_n--;return true;}}private:vector<HashData<K, V>> _tables;size_t _n = 0;		//有效数据个数};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/50129.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI大模型大厂面试真题:「2024大厂大模型技术岗内部面试题+答案」

AI大模型岗的大厂门槛又降低了&#xff01;实在太缺人了&#xff0c;大模型岗位真的强烈建议各位多投提前批&#xff0c;▶️众所周知&#xff0c;2025届秋招提前批已经打响&#xff0c;&#x1f64b;在这里真心建议大家6月7月一定要多投提前批&#xff01; &#x1f4bb;我们…

树莓派_Opencv学习笔记23:模版样本匹配

今日继续学习树莓派4B 4G&#xff1a;&#xff08;Raspberry Pi&#xff0c;简称RPi或RasPi&#xff09; 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: ​ Opencv 版本是4.5.1&#xff1a; ​ Python 版本3.7.3&#xff1a; 今日学习Opencv样本…

MongoDB - 集合方法 db.collection.find()

文章目录 1. query 参数2. projection 参数2.1 仅返回指定的字段2.2 排除字段2.3 显式排除_id字段 3. 修改游标行为3.1 为结果集中的文档排序3.2 限制要返回的文档数量3.3 控制结果集的起点3.4 组合游标方法 db.collection.find( <query>, <projection>, <optio…

【日常记录】【插件】Typed.js:用于创建打字效果的 JavaScript 库

文章目录 1. 引言2. 安装3. 基本使用参考链接 1. 引言 Typed.js是一个用于创建打字效果的 JavaScript 库。这个效果就是 chatgpt、百度的文心一言等其他的大模型&#xff0c;回复用户的问题的时候的效果 typed-js 官网typed 案例 2. 安装 CDN方式 这俩都可以&#xff0c;还有其…

18.springboot整合swagger

springboot整合swagger 引入依赖 <!--swagger--> <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version> </dependency> <dependency><groupId&…

Postman设置全部请求都携带请求头,Postman如何一次性设置请求头、不需要一个请求一个请求去添加请求头

文章目录 一、问题描述二、解决办法三、应用场景 一、问题描述 现在我有 n 个接口测试&#xff0c;其中 n 个都需要携带一致的请求头&#xff08;其实一般都是携带 JWT 令牌&#xff09;&#xff0c;怎么办&#xff1f;我要一个一个写&#xff1f;如图&#xff1a; 二、解决办…

C#从Socket里获取IP地址和端口号

在C#中&#xff0c;可以使用Socket类的RemoteEndPoint属性来获取连接的远程IP地址和端口号。以下是一个简单的示例代码&#xff0c;展示了如何从一个已连接的Socket实例中提取IP地址和端口号&#xff1a; using System; using System.Net; using System.Net.Sockets;public cl…

添加sidecar容器并输出日志

添加一个sidecar容器(使用busybox 镜像)到已有的pod 11-factor-app中,确保sidecar容器能够输出/var/log/11-factor-app.log的信息,使用volume挂载/var/log目录,确保sidecar能访问11-factor-app.log 文件 # 准备工作 创建一个 pod 11-factor-appapiVersion: v1 kind: Pod metada…

PDF管理器和查看器PdfDing

什么是 PdfDing &#xff1f; PdfDing 是一款自托管 PDF 管理器和查看器&#xff0c;可在多种设备上提供无缝用户体验。它设计精简、速度快&#xff0c;并且易于通过 Docker 设置。 功能特点 在多种设备上无缝基于浏览器的 PDF 查看使用标签整理 PDF干净且响应迅速的用户界面暗…

VulnHub:colddbox easy

靶机下载地址 信息收集 主机发现 攻击机网段192.168.31.0/24&#xff0c;扫描同网段存活主机。 nmap 192.168.31.0/24 -Pn -T4 发现靶机&#xff0c;IP为192.168.31.176。 端口扫描 扫描靶机开放端口。 nmap 192.168.31.176 -A -p- -T4 开放了80,4512端口&#xff0c;注…

Prometheus安装部署

文章目录 1.Prometheus(普罗米修斯)安装部署1.1部署环境准备1.2部署prometheus1.3主机数据展示 2.Grafana安装部署2.1部署Grafana2.2配置Grafana数据源2.2配置Grafana仪表板 3.AlertManager安装部署3.1部署alertmanager3.2告警邮件发送配置3.3测试邮件告警效果3.4自定义邮件告警…

ModuleNotFoundError: No module named ‘py3langid‘ 以及如何将包安在虚拟环境下

前提&#xff1a;已经安装过改包&#xff08;pip install py3langid&#xff09;&#xff0c;但仍报错 原因&#xff1a;安装在其他目录下了 解决办法&#xff1a; 1、再次在终端输入pip install py3langid 显示安装位置 Requirement already satisfied: py3langid in c:\…

【ESP8684————固件烧录说明、调试记录(1)】

环境&#xff1a; 硬件&#xff1a;ESP8684 模块 芯片&#xff1a;ESP8684-MINI-1U 固件&#xff1a;ESP32-C2-4MB-AT-V3.3.0.0 固件烧录及烧录说明整理&#xff1a;固件及烧录说明整理 一、调试笔记&#xff1a; 1&#xff09;关于ESP8684模块&#xff08;ATCWMODE&#xf…

【Python】快速创建一个简易 HTTP 服务器(http.server)

目录 官方文档安装教程用命令行创建编写代码创建 实例 官方文档 http.server 警告&#xff1a; http.server 不推荐用于生产环境。它仅仅实现了 basic security checks 的要求。 安装 Python3 内置标准模块&#xff0c;无需安装。&#xff08;在之前的 Python2 版本名称是 Si…

Jmeter三种方式获取数组中多个数据并将其当做下个接口参数入参【附带JSON提取器和CSV格式化】

目录 一、传统方式-JOSN提取器获取接口返回值 1、接口调用获取返回值 2、添加JSON提取器 3、调试程序查看结果 4、添加循环控制器 5、设置count计数器 6、添加请求 7、执行请求 二、CSV参数化 1、将结果写入后置处理程序 2、设置循环处理器 3、添加CSV文件 4、设置…

C++通过JNI传递AssetManager读取安卓assets目录中文件

在 Android 设备上&#xff0c;assets 目录位于应用程序的 APK 包内部&#xff0c;它并不直接暴露在设备的文件系统中。因此&#xff0c;无法通过传统的文件路径直接访问 assets 目录下的文件。访问 assets 目录下的文件通常通过 AssetManager 来实现。 就可以实现将C需要的文…

计算机网络中的 CDN 与内容分发网络

引言 在现代互联网中&#xff0c;用户期望能够快速、稳定地访问各种在线内容。内容分发网络&#xff08;Content Delivery Network&#xff0c;简称 CDN&#xff09;应运而生&#xff0c;成为解决这一需求的关键技术。CDN 是一种分布式的服务器系统&#xff0c;通过将内容缓存…

智税集成2.0生成凭证

:::info &#x1f4a1; 整体业务流程 从A9服务器中取数&#xff0c;生成列表数据&#xff0c;写入到对方oracle数据库中。 ::: 项目关键点 1.连接数据库 左连接连接本地SQLserver数据库、右连接要链接A9开票服务器的数据库然后设想用SQLserver 自带的外部连接来连接oracle数据…

机器学习·线性回归

线性回归 损失函数 J ( w , b ) 1 2 m ∑ i 1 m ( f ( i ) ( w 1 , w 2 , … , w j , b ) − y ( i ) ) 2 J(w,b)\frac{1}{2m}\sum_{i1}^{m}\left({f^{(i)}(w_1,w_2,\ldots,w_j,b)-y^{(i)}} \right)^2 J(w,b)2m1​i1∑m​(f(i)(w1​,w2​,…,wj​,b)−y(i))2 梯度下降方法 …

【资料分享】2024第三届钉钉杯大学生大数据挑战赛B题思路解析+双语言代码

2024钉钉杯大学生大数据挑战赛&#xff0c;B题解题思路和双语言代码分享&#xff0c;资料预览&#xff1a;