news 2026/5/16 13:27:16

【C++】哈希表的实现(链地址法)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【C++】哈希表的实现(链地址法)

1.其他哈希函数

1.1 乘法散列法(了解)

乘法散列法对哈希表⼤⼩M没有要求,他的⼤思路:

  • 第⼀步:⽤关键字K 乘常数 A(0<A<1),并抽取出k*A⼩数部分
  • 第⼆步:⽤M乘以k*A 的⼩数部分,再向下取整

其实就是让M去乘一个[0, 1)之间的小数,这个小数要与K相关,K不同,这个小数就不同,就能映射的更分散。

经过大佬Knuth的分析,建议这个A取黄金分割点:

A=(\sqrt{5}-1)/2 = 0.6180339887....

所以乘法散列法的公式为:h(key) = floor(M * (A * key) % 1.0)),其中floor表⽰对表达式进⾏向下取整,A∈(0,1)。

举个例子:乘法散列法对哈希表⼤⼩M是没有要求的,假设M为1024,key为1234,A = 0.6180339887, A*key = 762.6539420558,取⼩数部分为0.6539420558, M×((A×key)%1.0) = 0.6539420558*1024 = 669.6366651392,那么h(1234) = 669。

1.2 全域散列法(了解)

这个函数主要是为了抵御恶意攻击的。如果存在⼀个恶意的对⼿,他针对我们提供的散列函数,特意构造出⼀个发⽣严重冲突的数据集, ⽐如,让所有关键字全部落⼊同⼀个位置中。这种情况是可以存在的,只要散列函数是公开且确定的,就可以实现此攻击。

解决⽅法就是给散列函数增加随机性,攻击者就⽆法找出确定可以导致最坏情况的数据。这种⽅法叫做全域散列。

全域散列法的公式为:h(key) = ((a * key + b) % P) % M,P需要选一个足够大的质数,a可以随机选[1, P-1]之间的任意整数,b可以随机选[0, P-1]之间的任意整数。

这些函数构成了一个P * (P - 1)组全域散列函数组

假设key是8,P=17,M=6,a = 3, b = 4, 则h(8) = ((3 * 8 + 4) % 17) % 6 = 5。

需要注意的是每次初始化哈希表 时 ,随机选取全域散列函数组中的⼀个散列函数使⽤,后续增删查改都固定使⽤这个散列函数,否则每次哈希都是随机选⼀个散列函数,那么插⼊是⼀个散列函数,查找⼜是另⼀个散列函数,就会导致找不到插⼊的key了。

2.处理哈希冲突的方法

在开放定址法中所有的元素都放到哈希表⾥,当⼀个关键字key⽤哈希函数计算出的位置冲突了,则按照某种规则找到⼀个没有存储数据的位置进⾏存储,开放定址法中负载因⼦⼀定是⼩于的。这⾥的规则有三种:线性探测(已做讲解)、⼆次探测、双重探测

2.1 开放定址法
  • 性探测的⽐较简单且容易实现,线性探测的问题假设,hash0位置连续冲突,hash0,hash1,hash2位置已经存储数据了,后续映射到hash0,hash1,hash2,hash3的值都会争夺hash3位置,这种现象叫做群集/堆积
二次探测
  • 从发⽣冲突的位置开始,依次左右按加减⼆次⽅跳跃式探测,直到寻找到下⼀个没有存储数据的位置为⽌,如果往右⾛到哈希表尾,则回绕到哈希表头的位置;如果往左⾛到哈希表头,则回绕到哈希表尾的位置。
  • h(key) = hash0 = key % M, hash0的位置冲突了,则二次探测的公式为hc(key, i) = hashi = (hash0

\pm

i^{2}

) % M, i = { 1, 2, 3, ... ,

\frac{M}{2}

}。

  • 二次探测hashi = (hash0 -

i^{2}

) % M 时,hashi<0时,需要hashi += M。

把 {19,30,52,63,11,22} 等这⼀组值映射到M=11的表中。

h(19) = 8, h(30) = 8, h(52) = 8, h(63) = 8, h(11) = 0, h(22) = 0

代码就不实现了。

双重探测(了解):

线性探测是挨着找空位,二次探测是有规律的跳跃着找空位,双重探测就是再弄一个哈希函数,无规律的跳跃着找空位,减少堆积。

第⼀个哈希函数计算出的值发⽣冲突,使⽤第⼆个哈希函数计算出⼀个跟key相关的偏移量值,不

断往后探测,直到寻找到下⼀个没有存储数据的位置为⽌。

h1 (key) =hash0 =key%M, hash0位置冲突了,则双重探测公式为:

hc(key,i) =hashi= (hash0 +ih2 (key)) %Mi= {1, 2, 3, ...,M}

2.2 链地址法

前面的开放定址法不管是哪种探测,还是会出现相互挤占位置的情况,不能完全解决这个问题。更好的解决方法其实是链地址法

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

比如说把 {19,30,5,36,13,20,21,12,24,96} 等这⼀组值映射到M=11的表中。

h(19) = 8 , h(30) = 8 , h(5) = 5 , h(36) = 3 , h(13) = 2 , h(20) = 9 , h(21) = 10, h(12) = 1, h(24) = 2, h(96) = 8

开放定址法负载因⼦必须⼩于1,链地址法负载因⼦就没有限制了,可以⼤于1。

  • 负载因⼦越⼤,哈希冲突的概率越⾼,空间利⽤率越⾼;
  • 负载因⼦越⼩,哈希冲突的概率越低,空间利⽤率越低;
  • stl中unordered_xxx的最⼤负载因⼦基本控制在1,⼤于1就扩容


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/16 13:25:06

古城镇消防供水管网脆弱性与维护策略【附代码】

✨ 长期致力于古城镇消防供水管网、脆弱性、最小隔离单元、隔离阀故障、消火栓研究工作&#xff0c;擅长数据搜集与处理、建模仿真、程序编写、仿真设计。 ✅ 专业定制毕设、代码 ✅ 如需沟通交流&#xff0c;点击《获取方式》 &#xff08;1&#xff09;基于Johnson最短路径的…

作者头像 李华
网站建设 2026/5/16 13:25:06

告别环境配置烦恼:用QEMU User Mode快速验证你的aarch64交叉编译结果

告别环境配置烦恼&#xff1a;用QEMU User Mode快速验证你的aarch64交叉编译结果 在嵌入式开发和跨平台软件开发中&#xff0c;为ARM架构&#xff08;特别是aarch64&#xff09;交叉编译程序是常见需求。但许多开发者面临一个尴尬的现实&#xff1a;虽然能在x86主机上轻松完成…

作者头像 李华
网站建设 2026/5/16 13:24:55

基于RK3588核心板的8K全景相机系统设计与实战调优

1. 项目概述&#xff1a;为什么8K全景相机需要一颗“强心脏”这几年&#xff0c;VR内容创作的热度肉眼可见&#xff0c;从专业影视团队到个人Vlogger&#xff0c;手里没台全景相机&#xff0c;好像都少了点探索世界的视角。大家追求的&#xff0c;早已不是简单的“拍得到”&…

作者头像 李华
网站建设 2026/5/16 13:24:54

从原理到实现:基于FPGA的FIR滤波器全流程开发指南

1. FIR数字滤波器基础原理 FIR&#xff08;有限长单位冲激响应&#xff09;滤波器是数字信号处理中最常用的滤波器类型之一。它的核心特点在于系统响应只依赖于有限个输入样本&#xff0c;这使得它在硬件实现上具有先天优势。我第一次接触FIR滤波器是在一个音频处理项目中&…

作者头像 李华
网站建设 2026/5/16 13:24:02

如何快速将B站缓存视频转换为MP4:m4s-converter完整教程

如何快速将B站缓存视频转换为MP4&#xff1a;m4s-converter完整教程 【免费下载链接】m4s-converter 一个跨平台小工具&#xff0c;将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否曾经在B站缓存了重要的…

作者头像 李华
网站建设 2026/5/16 13:22:55

GEE实战指南:从数据导出到本地分析,掌握SHP与CSV的Export全流程

1. GEE数据导出基础&#xff1a;为什么需要本地分析&#xff1f; Google Earth Engine&#xff08;GEE&#xff09;作为强大的地理空间分析平台&#xff0c;虽然提供了云端计算能力&#xff0c;但实际项目中我们经常需要将数据导出到本地。最常见的原因包括&#xff1a;需要与其…

作者头像 李华