【哈希表】深入理解哈希表

news2024/9/20 6:07:36

目录

  • 1、哈希表简介
  • 2、哈希函数
    • 2.1、概念
    • 2.2、常用的哈希函数
      • 2.2.1、直接定址法
      • 2.2.2、除留余数法
      • 2.2.3、平方取中法
      • 2.2.4、基数转换法
  • 3、哈希冲突
    • 3.1、概念
    • 3.2、开放地址法【闭散列:key存放到冲突位置的“下一个”空位置】
    • 3.3、链地址法【开散列:冲突位置变为链表】
    • 3.4、开散列下冲突严重时(导致链表过长)的优化
      • 3.4.1、整个哈希表进行扩容
      • 3.4.2、单个链表转为哈希表或者变为搜索树
  • 4、负载因子

1、哈希表简介

哈希表(Hash Table):也叫做散列表,是根据关键码值(Key Value)直接进行访问的数据结构,可快速查找

实现思想:

哈希表通过「键 key 」和「映射函数 Hash(key) 」计算出对应的「值 value」,把关键码值映射到表中一个位置来访问记录,以加快查找的速度。
这个映射函数叫做「哈希函数(散列函数)」,存放记录的数组叫做「哈希表(散列表)」

可以将算法思想分为两个部分:

  • 向哈希表中插入一个关键码值:哈希函数决定该关键字的对应值应该存放到表中的哪个区块,并将对应值存放到该区块中
  • 在哈希表中搜索一个关键码值:使用相同的哈希函数从哈希表中查找对应的区块,并在特定的区块搜索该关键字对应的值

案例:使用 value = Hash(key) = key // 1000 作为哈希函数【// 整除】

在这里插入图片描述

  • 向哈希表中插入一个关键码值:通过哈希函数解析关键字,并将对应值存放到该区块中
    • 如:0138 通过哈希函数 Hash(key) = 0138 // 100 = 0,得出应将 0138 分配到 0 所在的区块中
  • 在哈希表中搜索一个关键码值:通过哈希函数解析关键字,并在特定的区块搜索该关键字对应的值
    • 如:查找 2321,通过哈希函数,得出 2321 应该在 2 所对应的区块中。然后我们从 2 对应的区块中继续搜索,并在 2 对应的区块中成功找到了 2321

哈希表的优势:

在顺序结构和平衡树中,元素关键码与它的存储位置之间没有对应映射关系,在查找时需要多次比较,而哈希表可以通过哈希函数建立元素关键码与存储位置之间的对应关系,查找时经过哈希函数的计算,可以算出元素对应的存储位置,直接到存储位置取出要查找的元素,不需要像顺序结构或者平衡树那样多次比较

2、哈希函数

2.1、概念

哈希函数(Hash Function):将哈希表中元素的关键键值映射为元素存储位置的函数

一般来说,哈希函数会满足以下几个条件:

  • 哈希函数应该易于计算,并且尽量使计算出来的索引值均匀分布
  • 哈希函数计算得到的哈希值是一个固定长度的输出值
  • 如果 Hash(key1) 不等于 Hash(key2),那么 key1、key2 一定不相等
  • 如果 Hash(key1) 等于 Hash(key2)那么 key1、key2 可能相等,也可能不相等(会发生哈希碰撞)

在哈希表的实际应用中,关键字的类型除了数字类,还有可能是字符串类型、浮点数类型、大整数类型,甚至还有可能是几种类型的组合。一般我们会将各种类型的关键字先转换为整数类型,再通过哈希函数,将其映射到哈希表中

关于整数类型的关键字,通常用到的哈希函数方法有:直接定址法、除留余数法、平方取中法、基数转换法、数字分析法、折叠法、随机数法、乘积法、点积法等

2.2、常用的哈希函数

2.2.1、直接定址法

直接定址法:取 关键字本身 / 关键字的某个线性函数值 作为哈希地址。即:Hash(key) = key 或者 Hash(key) = a * key + b,其中 a 和 b 为常数

这种方法计算最简单,且不会产生冲突。适合于关键字分布基本连续的情况,如果关键字分布不连续,空位较多,则会造成存储空间的浪费

案例:我们有一个记录了从 1 岁到 100 岁的人口数字统计表。其中年龄为关键字,哈希函数取关键字自身,如下表所示

年龄1299100
人数200030005000

2.2.2、除留余数法

除留余数法:假设哈希表的表长为 m,取一个不大于 m 但接近或等于 m 的质数 p,利用取模运算,将关键字转换为哈希地址。即:Hash(key) = key % p,其中 p 为不大于 m 的质数

这是一种简单且常用的哈希函数方法。其关键点在于 p 的选择。根据经验而言,一般 p 取素数或者 m,这样可以尽可能的减少冲突

案例:需要将 7 个数 [432, 5, 128, 193, 92, 111, 88] 存储在 11 个区块中(长度为 11 的数组),通过除留余数法将这 7 个数应分别位于如下地址:

第一个数计算出索引位置:432 % 11 = 3

数据88111432925193128
索引012345678910

2.2.3、平方取中法

平方取中法:先通过求关键字平方值的方式扩大相近数之间的差别,然后根据表长度取关键字平方值的中间几位数为哈希地址

如:Hash(key) = (key * key) // 100 % 1000,先计算平方,去除末尾的 2 位数,再取中间 3 位数作为哈希地址

这种方法因为关键字平方值的中间几位数和原关键字的每一位数都相关,所以产生的哈希地址也比较均匀,有利于减少冲突的发生

2.2.4、基数转换法

基数转换法:将关键字看成另一种进制的数再转换成原来进制的数,然后选其中几位作为哈希地址

如:将关键字看做是 13 进制的数,再将其转变为 10 进制的数,将其作为哈希地址。以 343246 为例:

34324613 = 3 * 13 5 + 4 * 13 4 + 3 * 13 3 + 2 * 13 2 + 4 * 13 1 + 6 * 13 0 = 123511010

3、哈希冲突

3.1、概念

哈希冲突:不同的关键字通过同一个哈希函数可能得到同一哈希地址,即 key1 ≠ key2,而 Hash(key1) = Hash(key2),这种现象称为哈希冲突

理想状态下,我们的哈希函数是完美的一对一映射,即一个关键字(key)对应一个值(value),不需要处理冲突。但是一般情况下,不同的关键字 key 可能对应了同一个值 value,这就发生了哈希冲突

设计再好的哈希函数也无法完全避免哈希冲突。所以就需要通过一定的方法来解决哈希冲突问题。

常用的哈希冲突解决方法主要是两类:

  • 开放地址法
  • 链地址法

3.2、开放地址法【闭散列:key存放到冲突位置的“下一个”空位置】

开放地址法(Open Addressing):指的是将哈希表中的「空地址」向处理冲突开放。当哈希表未满时,处理冲突时需要尝试另外的单元,直到找到空的单元为止

当发生冲突时,开放地址法按照下面的方法求得后继哈希地址:H(i) = (Hash(key) + F(i)) % m,i = 1, 2, 3, …, n (n ≤ m - 1)

  • H(i):在处理冲突中得到的地址序列。即在第 1 次冲突(i = 1)时经过处理得到一个新地址 H(1),如果在 H(1) 处仍然发生冲突(i = 2)时经过处理时得到另一个新地址 H(2) …… 如此下去,直到求得的 H(n) 不再发生冲突
  • Hash(key):哈希函数,m 是哈希表表长,对哈希表长取余的目的是为了使得到的下一个地址一定落在哈希表中
  • F(i) :冲突解决方法,取法可以有以下几种
    • 线性探测法: F ( i ) = 1 , 2 , 3 , . . . , m − 1 F(i) = 1, 2, 3, ..., m - 1 F(i)=1,2,3,...,m1
    • 二次探测法: F ( i ) = 1 2 , − 1 2 , 2 2 , − 2 2 , . . . , ± n 2 ( n ≤ m / 2 ) F(i) = 1^2, -1^2, 2^2, -2^2, ..., \pm n^2(n \le m / 2) F(i)=12,12,22,22,...,±n2(nm/2)。 【负数表示向前,正数表示向后】
    • 伪随机数序列: F ( i ) = 伪随机数序列 F(i) = 伪随机数序列 F(i)=伪随机数序列

案例:在长度为 11 的哈希表中已经填有关键字分别为 28、49、18 的记录(哈希函数为 Hash(key) = key % 11)。现在将插入关键字为 38 的新纪录。根据哈希函数得到的哈希地址为 5,产生冲突。

接下来分别使用这三种冲突解决方法处理冲突:

  • 线性探测法:得到下一个地址 H(1) = (5 + 1) % 11 = 6,仍然冲突;继续求出 H(2) = (5 + 2) % 11 = 7,仍然冲突;继续求出 H(3) = (5 + 3) % 11 = 8,8 对应的地址为空,处理冲突过程结束,记录填入哈希表中序号为 8 的位置
  • 二次探测法:得到下一个地址 H(1) = (5 + 11) % 11 = 6,仍然冲突;继续求出 H(2) = (5 - 11) % 11 = 4,4 对应的地址为空,处理冲突过程结束,记录填入哈希表中序号为 4 的位置
  • 伪随机数序列:假设伪随机数为 9,则得到下一个地址 H(1) = (9 + 5) % 11 = 3,3 对应的地址为空,处理冲突过程结束,记录填入哈希表中序号为 3 的位置

如下图:

在这里插入图片描述

闭散列方式最大的问题就在于,若整个哈希表冲突比较严重,此时查找元素的过程就相当于遍历数组,查找效率退化为O(N)

3.3、链地址法【开散列:冲突位置变为链表】

链地址法(Chaining):将具有相同哈希地址的元素(或记录)存储在同一个线性链表中

假设哈希函数产生的哈希地址区间为 [0, m - 1],哈希表的表长为 m。则可以将哈希表定义为一个有 m 个头节点组成的链表指针数组 T:

  • 这样在插入关键字的时候,我们只需要通过哈希函数 Hash(key) 计算出对应的哈希地址 i,然后将其以链表节点的形式插入到以 T[i] 为头节点的单链表中。在链表中插入位置可以在表头或表尾,也可以在中间。如果每次插入位置为表头,则插入操作的时间复杂度为 O ( 1 ) O(1) O(1)
  • 而在在查询关键字的时候,我们只需要通过哈希函数 Hash(key) 计算出对应的哈希地址 i,然后将对应位置上的链表整个扫描一遍,比较链表中每个链节点的键值与查询的键值是否一致。查询操作的时间复杂度跟链表的长度 k 成正比,也就是 O ( k ) O(k) O(k)。对于哈希地址比较均匀的哈希函数来说,理论上讲,k = n // m,其中 n 为关键字的个数,m 为哈希表的表长

案例:现在要存入的关键字集合 keys = [88, 60, 65, 69, 90, 39, 07, 06, 14, 44, 52, 70, 21, 45, 19, 32]。再假定哈希函数为 Hash(key) = key % 13,哈希表的表长 m = 13,哈希地址范围为 [0, m - 1]。将这些关键字使用链地址法处理冲突,并按顺序加入哈希表中(图示为插入链表表尾位置),最终得到的哈希表如下图所示:

在这里插入图片描述

相对于开放地址法,采用链地址法处理冲突要多占用一些存储空间(主要是链节点占用空间)。但它可以减少在进行插入和查找具有相同哈希地址的关键字的操作过程中的平均查找长度。这是因为在链地址法中,待比较的关键字都是具有相同哈希地址的元素,而在开放地址法中,待比较的关键字不仅包含具有相同哈希地址的元素,而且还包含哈希地址不相同的元素

3.4、开散列下冲突严重时(导致链表过长)的优化

3.4.1、整个哈希表进行扩容

假设以前%7(数组长度为7),现在就扩容为原数组的一倍,现在%14(数组长度变为14),就可以大大降低冲突的概率,减少链表长度

3.4.2、单个链表转为哈希表或者变为搜索树

单个链表长度过长,查询效率就会变为链表的遍历 O(N),就针对这单个链表进行转换处理:可以把单个链表转为哈希表或者变为搜索树(JDK8 的 HashMap 就采用此方案,当某个链表的长度 >8 且整个哈希表元素个数 >64,把此链表转为红黑树

4、负载因子

散列表的载荷因子定义为: a = 填入表中的元素个数 / 散列表的长度

  • 负载因子越大,发生哈希冲突的概率越大,数组长度就会偏小,但节省空间
  • 负载因子越小,发生哈希冲突的概率越小,数组长度就会偏大,但浪费空间

负载因子和冲突率是相互影响的,因此可以设定一个的负载因子阈值,负载因子超过阈值,就增加哈希桶,降低冲突率

在这里插入图片描述

由图中可以看到,负载因子的值增大,冲突率也随着增大,我们不能直接控制冲突率,可以通过影响负载因子来降低冲率,而控制负载因子,负载因子是哈希表的元素数量除哈希桶数量,我们认为哈希表要传入的数量是未知的,也可以看作无穷的,所以,通过不能降低减少哈希表元素的数量来降低负载因子的值,但我们可以通过增加哈希桶的值来降低负载因子的值,进而降低冲突率【HashMap 的哈希表长度为16,当元素个数超过 12 就会发生扩容】

【参考资料】
算法数据结构基础——哈希表(Hash Table)
数据结构之—哈希表

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

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

相关文章

SAM 2:分割图像和视频中的任何内容

文章目录 摘要1 引言2 相关工作3 任务:可提示视觉分割4 模型5 数据5.1 数据引擎5.2 SA-V数据集 6 零样本实验6.1 视频任务6.1.1 提示视频分割6.1.2 半监督视频对象分割6.1.3 公平性评估 6.2 图像任务 7 与半监督VOS的最新技术的比较8 数据和模型消融8.1 数据消融8.2…

dr 航迹推算 知识介绍

DR(Dead Reckoning)航迹推算是一种在航海、航空、车辆导航等领域中广泛使用的技术,用于估算物体的位置。DR航迹推算主要通过已知的初始位置和运动参数(如速度、方向)来预测物体的当前位置。以下是 DR 航迹推算的详细知…

跨平台电商数据整合:item_get API在电商大数据平台中的角色

在电商行业蓬勃发展的今天,跨平台运营已成为众多商家的必然选择。然而,随之而来的数据孤岛问题却成为了制约电商企业进一步发展的瓶颈。为了解决这一问题,电商大数据平台应运而生,而item_get API作为获取商品详情的关键接口&#…

统计学第6天

1、变量间关系的度量 函数关系 (1)是一一对应的确定关系; (2)设有两个变量x和y,变量y随x一起变化,并完全依赖于x,当x取某个数值时,y根据确定的关系取相应的值,称y是x的…

建设网盘聚合中心—Win10+Alist+RaiDrive

经常需要在网上找各种资源,但遇到 2 个问题: 1. 大部分网盘都需要先将文件保存在自己网盘后才能下载,也就是必须创建对应网盘账号。 2. 有些网盘还必须要下载客户端才能下载文件。 创建账号无法避免,但可以不用下载那么多的客户端…

写在 Pencils Protocol TGE 前:加密市场共识才是王道,拥抱社区

“Pencils Protocol 正在成为本轮市场周期中,加密项目建立共识最有力的工具!” 对于加密项目而言,代币 TGE 是一个非常重要的事情,它不仅仅意味着生态内经济系统的全面启动,同时也意味着项目生态市场的全面开启。当然…

《Rust避坑式入门》第1章:挖数据竞争大坑的滥用可变性

赵可菲是一名Java程序员,一直在维护一个有十多年历史的老旧系统。这个系统即将被淘汰,代码质量也很差,每次上线都会出现很多bug,她不得不加班修复。公司给了她3个月的内部转岗期,如果转不出去就会被裁员。她得知公司可…

AF透明模式/虚拟网线模式组网部署

透明模式组网 实验拓扑 防火墙基本配置 接口配置 eth1 eth3 放通策略 1. 内网用户上班时间(9:00-17:00)不允许看视频、玩游戏及网上购物,其余时 间访问互联网不受限制;(20 分) 应用控制策略 2. 互联…

二维空间向量的p范数等密度轨迹

图2-52:二维空间向量的 ℓ p \ell p ℓp范数等密度轨迹。 想过两种方式,还是放在一起省地方。 禹晶、肖创柏、廖庆敏《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》 禹晶、肖创柏、廖庆敏《数字图像处理》资源…

数据库系统原理及应用——仓库管理系统

目录 引言 一.需求设计说明书 1.需求分析 2.系统背景 3.系统目标 4.人员分配 5.数据流程图(DFD) 二.概念结构设计 1.局部E-R图 (1)供应商 (2)货物 (3)客户 &…

1-19 平滑处理——双边滤波 opencv树莓派4B 入门系列笔记

目录 一、提前准备 二、代码详解 cv2.bilateralFilter函数用于对图像进行双边滤波。双边滤波是一种保持边缘的平滑技术,常用于图像去噪声和增强图像的细节。函数的四个参数如下: 三、运行现象 四、完整工程贴出 一、提前准备 1、树莓派4B 及 64位系统…

stack smashing detect以及解决之道

0. 简介 相较于其他报错,stack smashing detect这个报错是最令人头疼的段错误种类。“Stack smashing detect” 是指在程序运行过程中检测到栈溢出的情况。栈溢出是一种常见的安全漏洞,发生在程序尝试往栈空间写入超过其边界范围的数据时。 1. 常见分类…

改写二进制文件

以下是一些常见的方法和工具: 1. 使用十六进制编辑器 十六进制编辑器 是最直接的工具之一,用于查看和编辑二进制文件中的数据。它允许你以十六进制格式查看和修改文件内容。 常见十六进制编辑器: HxD(Windows)Hex F…

铁路订票系统小程序的设计

管理员账户功能包括:系统首页,个人中心,管理员管理,车次信息管理,基础数据管理,论坛管理,通知公告管理,用户管理,轮播图信息 微信端账号功能包括:系统首页&a…

【linux学习指南】Linux编译器 gcc和g++使用

文章目录 📝前言🌠 gcc如何完成🌉预处理(进行宏替换) 🌠编译(生成汇编)🌉汇编(生成机器可识别代码) 🌠链接(生成可执行文件或库文件)&…

变压器制造5G智能工厂工业物联数字孪生平台,推进制造业数字化转型

变压器制造5G智能工厂工业物联数字孪生平台,推进制造业数字化转型。作为传统制造业的重要组成部分,变压器制造行业也不例外地踏上了数字化转型的快车道。而变压器制造5G智能工厂物联数字孪生平台的出现,更是为这一进程注入了强大的动力&#…

内卷时代无人机培训机构如何做大做强

在当今社会,随着科技的飞速发展,“内卷”一词频繁被提及,反映了各行业竞争日益激烈的现象。对于无人机培训行业而言,如何在这样的时代背景下脱颖而出,实现做大做强的目标,成为每个培训机构必须深思的问题。…

自学C语言-11

** 第3篇 高级应用 ** 第11章 结构体和共用体 迄今为止,我们在程序中用到的都是基本数据类型。但实际开发中,有时简单的变量类型无法满足程序中各种复杂的数据要求,因此C语言还提供了构造类型。构造类型数据是由基本类型数据按照一定规则组成的。 本章致力于使读者了解结…

【Nginx系列】Nginx中rewrite模块

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

《战锤40K:星际战士2》超越《黑神话》 登Steam热销榜首

《使命召唤:黑色行动6》将登陆 PC Game Pass看来确实影响了销量,因为这次在 Steam 上它的预购并没有占领 Steam 热销榜单之首。这次霸榜的则是即将推出的《战锤40K:星际战士2》。 根据 SteamDB 显示,这部将于9 月 10 日发售的游戏…