【C++】哈希表实现

1. 哈希概念

哈希(hash)又称散列,是⼀种组织数据的方式。从译名来看,有散乱排列的意思。本质就是通过哈希 函数把关键字Key跟存储位置建立一个映射关系,查找时通过这个哈希函数计算出Key存储的位置,进行快速查找

1.1 直接定址法

当关键字的范围较集中时,直接定址法就是简单高效的方法,如⼀组关键字都在[0,99]之间, 那么开一个100个数的数组,每个关键字的值直接就是存储位置的下标。再如一组关键字值都在 [a,z]的小写字母,那么开一个26个数的数组,每个关键字acsii码-a 的ascii码就是存储位置的下标。 也就是说直接定址法本质就是用关键字计算出一个绝对位置或者相对位置

1.2 哈希冲突

两个不同的key可能会映射到同一个位置去,这种问题叫做哈希冲突, 或者哈希碰撞

1.3 负载因子

 假设哈希表中已经映射存储了N个值,哈希表的大小为M,那么负载因子=N/M ,负载因子有些地方也翻译为载荷因子/装载因子等,负载因子越大,哈希冲突的概率越高,空间利用率越高;负载因子越小,哈希冲突的概率越低,空间利用率越低

1.4 哈希函数

一个好的哈希函数应该让N个关键字被等概率的均匀的散列分布到哈希表的M个空间中,但是实际中却很难做到,尽量往这个方向去考量设计

除法散列法/除留余数法

  • 假设哈希表的大小为M,那么通过key除以M的余数作为映射位置的下标,也就是哈希函数为:h(key)=key%M
  • 当使用除法散列法时,要尽量避免M为某些值,如2的幂,10的幂等
  • 如果是2的幂,那么key % 2^x 本质相当于保留key的后X位,那么后x位相同的值,计算出的哈希值都是⼀样的,就冲突了。如果是10的幂 ,保留的都是 10进值的后x位
  • 当使用除法散列法时,建议M取不太接近2的整数次幂的一个质数(素数)

1.5 处理哈希冲突

主要有两种两种方法,开放定址法和链地址法

1.5.1 开放定址法

在开放定址法中所有的元素都放到哈希表里,当一个关键字key用哈希函数计算出的位置冲突了,则按照某种规则找到一个没有存储数据的位置进行存储,开放定址法中负载因子一定是小于的。有三种:线性探测、⼆次探测、双重探测

1)线性探测
  • 从发生冲突的位置开始,依次线性向后探测,直到寻找到下一个没有存储数据的位置为止,如果走到哈希表尾,则回绕到哈希表头的位置
  • ,hash0位置冲突了,则线性探测公式为: ,因为负载因子小于1, 则最多探测M-1次,一定能找到一个存储key的位置
  • 线性探测的比较简单且容易实现,线性探测的问题假设,hash0位置连续冲突,hash0,hash1, hash2位置已经存储数据了,后续映射到hash0,hash1,hash2,hash3的值都会争夺hash3位 置,这种现象叫做群集/堆积。二次探测可以一定程度改善这个问题

2)二次探测
  • 从发生冲突的位置开始,依次左右按二次方跳跃式探测,直到寻找到下一个没有存储数据的位置为 为,如果走到哈希表尾,则回绕到哈希表头的位置;如果往左走到哈希表头,则回绕到哈希表尾的位置
  • ,hash0位置冲突了,则二次探测公式为:
  • 二次探测当hashi = (hash0 − i )%M 时,当hashi<0时,需要hashi+=M

1.5.2 开放定址法代码实现

哈希表结构
enum State
{EXIST,EMPTY,DELETE
};template<class K, class V>
struct HashData
{pair<K, V> _kv;State _state = EMPTY;
};template<class K, class V>
class HashTable
{
private:vector<HashData<K, V>> _tables;size_t _n = 0; // 表中存储数据个数 
};
扩容

哈希表负载因子控制在0.7,当负载因子到0.7以后就需要扩容了,给了一个近似2倍的质数表,每次去质数表获取扩容后的大小

inline unsigned long __stl_next_prime(unsigned long n)
{// Note: assumes long is at least 32 bits.static const int __stl_num_primes = 28;static const unsigned long __stl_prime_list[__stl_num_primes] ={53, 97, 193, 389, 769,1543, 3079, 6151, 12289, 24593,49157, 98317, 196613, 393241, 786433,1572869, 3145739, 6291469, 12582917, 25165843,50331653, 100663319, 201326611, 402653189, 805306457,1610612741, 3221225473, 4294967291};const unsigned long* first = __stl_prime_list;const unsigned long* last = __stl_prime_list + __stl_num_primes;const unsigned long* pos = lower_bound(first, last, n);return pos == last ? *(last - 1) : *pos;
}
key不能取模的问题
  • 当key是string/Date等类型时,key不能取模,那么需要给HashTable增加一个仿函数,这个仿函数⽀持把key转换成一个可以取模的整形
  • 自己实现一个仿函数传给这个参数,让不同的key转换出的整形值不同
  • string 做哈希表的key很常见,可以考虑把string特化一下
template<class K>
class HashFunc
{
public:size_t operator()(const K& key){return (size_t)key;}
};template<>
class HashFunc<string>
{
public:// 字符串转换成整形,可以把字符ascii码相加即可 // 但是直接相加的话,类似"abcd"和"bcad"这样的字符串计算出是相同的 // 这⾥我们使⽤BKDR哈希的思路,⽤上次的计算结果去乘以一个质数,这个质数一般去31, 131
等效果会⽐较好size_t operator()(const string& key){size_t hash = 0;for (auto ch : key){hash += ch;hash *= 131;}return hash;}
};
template<class K, class V, class Hash = HashFunc<K>>
class HashTable
{
public:
private:vector<HashData<K, V>> _tables;size_t _n = 0; // 表中存储数据个数 
};
完整代码实现

开放地址法

1.5.3 链地址法

开放定址法中所有的元素都放到哈希表,链地址法中所有的数据不再直接存储在哈希表中,哈希表 中存储一个指针,没有数据映射这个位置时,这个指针为空,有多个数据映射到这个位置时,把这些冲突的数据链接成一个链表,挂在哈希表这个位置下面,链地址法也叫做拉链法或者哈希桶

扩容

开放定址法负载因子必须小于1,链地址法的负载因子就没有限制了,可以大于1

这里大于1就扩容

插入逻辑

链地址法代码实现

实现代码

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

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

相关文章

ai 玩游戏 llm玩街霸 大模型玩街霸 (3)

1. 开源代码地址&#xff1a; https://github.com/OpenGenerativeAI/llm-colosseum 2. 架构&#xff1a; 3. 图片&#xff1a; 4. 感觉还是下面的步骤&#xff1a; a. 实时理解游戏当前环境&#xff0c;英雄角色&#xff0c;英雄状态 b. 根据当前状态感知&#xff0c;生成英雄…

2025年渗透测试面试题总结-59(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 一、SQL注入全解 二、XSS与文件漏洞 三、服务端漏洞专题 四、职业经验与能力评估 1、注入攻击原理是什么…

GPT系列--类GPT2源码剖析

无需多言&#xff0c;大家应该都用过了&#xff0c;如今都更新到GPT-5了。1. GPT-1回到2018年的NLP&#xff0c;神仙打架&#xff0c;BERT与GPT不分先后。GPT是“Generative Pre-Training”的简称&#xff0c;生成式的预训练。BERT和GPT肯定是GPT难训练&#xff0c;引用量也是B…

这是一款没有任何限制的免费远程手机控制手机的软件

这是一款没有任何限制的免费远程手机控制手机的软件支持安卓和苹果1.安装1.1被控制端安装airdroid1.2控制端air mirror2.登录账号控制端和被控制端登录同一个账号3.控制打开控制端软件选择要控制的机器直接点“远程控制“

Observability:更智能的告警来了:更快的分诊、更清晰的分组和可操作的指导

作者&#xff1a;来自 Elastic Drew Post 探索 Elastic Stack 告警的最新增强功能&#xff0c;包括改进的相关告警分组、将仪表盘链接到告警规则&#xff0c;以及将调查指南嵌入到告警中。 在 9.1 版本中&#xff0c;我们对告警进行了重大升级&#xff0c;帮助 SRE 和运维人员更…

数智之光燃盛景 共同富裕创丰饶

8月29日&#xff0c;2025数博会“一带一路”国际大数据产业发展暨数智赋能新时代、共同富裕向未来的会议在贵阳国际生态会议中心隆重举行。作为全球大数据领域的重要盛会&#xff0c;此次活动吸引了来自联合国机构、国际组织、科研院所、知名企业等社会各界的百余位代表&#x…

【网络编程】recv函数的本质是什么?

一、为什么说recv函数的本质是 “copy”&#xff1f; recv是用于从网络连接&#xff08;或其他 IO 对象&#xff09;接收数据的函数&#xff0c;它的核心动作不是 “从网络上拉取数据”&#xff0c;而是 “把已经到达内核缓冲区的数据复制到用户程序的缓冲区”。 具体流程拆解&…

JSP程序设计之输入/输出对象 — out对象

目录1、out对象概述2.实例&#xff1a;out对象方法运用输入/输出对象&#xff0c;可以控制页面的输入和输出&#xff0c;用于访问与所有请求和响应有关的数据&#xff0c;包括out、request和response对象。 1、out对象概述 out对象是JspWriter类的一个实例&#xff0c;是一个…

UE里为什么要有提升变量

1、为了简洁当一个类里面的函数比较多&#xff0c;并且使用比较频繁的时候&#xff0c;就要不断的从这个类节点往外拉线&#xff0c;从而获取不同的函数节点&#xff0c;这样的蓝图就会看起来比较乱&#xff0c;这时候&#xff0c;就可以将这个常用的类提升为变量。2、为了存储…

玩转物联网只需十行代码,可它为何悄悄停止维护

文章目录玩转物联网只需十行代码&#xff0c;可它为何悄悄停止维护1 背景&#xff1a;MQTT 遇上 asyncio&#xff0c;为什么选 hbmqtt&#xff1f;2 hbmqtt 是什么&#xff1f;3 安装&#xff1a;一行命令&#xff0c;但别装最新4 五大核心 API&#xff1a;10 行代码跑通发布订…

从零开始学大模型之预训练语言模型

预训练语言模型 本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型开发 学习视频/籽料/面试题 都在这>>Github<< >>Gitee<< 3.1 Encoder-only PLM 在上一章&#xff0c;我们详细讲解了给 NLP 领域带来巨大变革注意力机制以及使用…

JMeter接口测试全流程解析

1. Jmeter的界面介绍和功能组件&#xff08;元件&#xff09;1、测试计划&#xff1a;Jmeter的起点和容器2、线程组&#xff1a;代表一定的虚拟用户&#xff08;一个用户一个线程&#xff09;3、取样器&#xff1a;发送请求的最小单元4、逻辑控制器&#xff1a;控制组件的执行顺…

Effective Modern C++ 条款26:避免在通用引用上重载

在C编程中&#xff0c;函数重载是一项强大的特性&#xff0c;它允许我们为不同的参数类型提供不同的实现。然而&#xff0c;当涉及到通用引用&#xff08;universal references&#xff09;时&#xff0c;重载可能会带来意想不到的问题。Effective Modern C的条款26明确指出&am…

OpenLayers数据源集成 -- 章节一:图像图层详解

前言在前面的文章中&#xff0c;我们学习了OpenLayers的基础控件操作。本文将深入探讨OpenLayers中的图像图层&#xff08;ImageLayer&#xff09;功能&#xff0c;通过一个完整的示例来展示如何使用ImageArcGISRest数据源加载ArcGIS服务&#xff0c;并详细解释图层配置、事件监…

通义万相wan2.2 Fun系列--Camera镜头控制与lnp首尾帧视频模型

上节内容讲解了wan2.2 fun control本节内容对wan2.2 fun系列模型的camera镜头控制模型与lnp首尾帧视频模型进行测试与讲解。 Wan2.2-Fun-Camera-Control是阿里基于Wan2.2框架推出的图生视频运镜控制模型 。它支持512、768、1024等多分辨率的视频预测&#xff0c;以81帧、每秒16…

JavaSE 集合从入门到面试:全面解析与实战指南

JavaSE 集合从入门到面试&#xff1a;全面解析与实战指南 在 Java 编程中&#xff0c;集合是处理数据的核心工具&#xff0c;几乎所有 Java 应用都会用到集合框架。从简单的列表存储到复杂的数据分析&#xff0c;集合框架提供了丰富的数据结构和操作方法。本文将从基础概念到面…

自建云音乐服务器:Navidrome+cpolar让无损音乐随身听

文章目录前言1. 安装Docker2. 创建并启动Navidrome容器3. 公网远程访问本地Navidrome3.1 内网穿透工具安装3.2 创建远程连接公网地址3.3 使用固定公网地址远程访问前言 “想听自己的无损音乐还要开会员&#xff1f;”——音乐发烧友小王的烦恼。商业音乐平台音质压缩&#xff…

C3P0连接池适配HGDB

文章目录文档用途详细信息文档用途 讲解常用的并且需要与数据库进行交互的开源框架C3P0&#xff0c;以及C3P0框架是如何适配HGDB的。 详细信息 1.C3P0概述 C3P0是一个开源的JDBC连接池&#xff0c;它实现了数据源和JNDI绑定&#xff0c;支持JDBC3规范和JDBC2的标准扩展。目…

ZeroGPU Spaces 加速实践:PyTorch 提前编译全解析

ZeroGPU 让任何人都能在 Hugging Face Spaces 中使用强大的 Nvidia H200 硬件&#xff0c;而不需要因为空闲流量而长期占用 GPU。 它高效、灵活&#xff0c;非常适合演示&#xff0c;不过需要注意的是&#xff0c;ZeroGPU 并不能在所有场景下完全发挥 GPU 与 CUDA 栈的全部潜能…

8.ImGui-输入框

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 上一个内容&#xff1a;7.ImGui-单选框和复选框 单行输入框使用 ImGui::InputText()&#xff0c;下图中…