数据结构 - 哈希表 - 哈希表介绍
如果要存储和使用线性表 ,一般情况下我们会使用一个数组 来顺序存储这些数。但是这样的存储结构会给查询算法带来 的时间开销。对数组 排序,使用二分查询法,时间复杂度变为 。也可以用空间换时间的做法,比如对值域建一个数组,这里用数组 来表示每个数是否出现,此时查找的时间复杂度变为 ,但是空间上的开销变得巨大。
哈希表的引入
让我们想一下,若在手机通信录中查找一个人,那我们应该不会从第一个人一直找下去,因为这样实在是太慢了。我们其实是这样做的:首先看这个人的名字的首字母是什么,比如姓张,那么我们一定会滑到最后,因为“Z”姓的名字都在最后。
还有在查字典时,要查找一个单词,肯定不会从头翻到尾,而是首先通过这个单词的首字母,找到对应的那一页;再找第 个字母、第 个字母……这样可以快速跳到那个单词所在的页。
对于引用部分给定的例子而言,我们使用哈希表来优化。先建立一个哈希函数 (这里的 表示模运算)。此时不管 有多大,都转化成了模数的范围内,对给定的线性表例子有
此时对值域建立数组,我们只要用一个 数组就可以快速的查询每个数是否出现。这种线性表的结构就称为哈希表(),也称散列表。对哈希表有一种需要重点讨论的情况,即对两个的 和 出现了
此时称这种情况为冲突。
哈希表的基本原理
可以看出,哈希表的基本原理是用一个下标范围比较大(下标范围太小容易冲突),相对原值域又比较小的数组 来存储元素(上述例子中的下标范围 相对于值域 )。
设计一个哈希函数 ,对于要存储的线性表的每个元素 ,取一个关键字 ,算出函数值 ,然后把这个值 作为下标,用 来存储 。
举一个例子,在手机通信录中查找一个人,这个人的姓名是“张三”(即 ),我们取该姓名的首字母"Z"作为关键字(即 ),然后利用哈希函数 得到哈希值 (即 ,哈希函数的映射规则表示为 "Z" 是第 个英文字母),然后在哈希表中找到 ,若用链地址法,可能还需再在 的链表中具体查找 “张三”。
例子所展示的过程如图所示:
哈希函数的选择
按我们之前的介绍,哈希函数需要满足以下条件
-
哈希函数计算得到的哈希值应该是一个非负整数(以便作为哈希表的索引或称下标)。
-
如果两个关键字 ,则 。
哈希函数比较常用的实现方法比较多,通常需要考虑几个因素:关键字的长度、哈希表的大小、关键字的分布情况、记录的查找频率,等等。
下面简单介绍几种哈希函数。
-
直接寻址法:取关键字或关键字的某个线性函数值为散列地址。
-
数字分析法:通过对数据的分析,发现数据中冲突较少的部分,并构造散列地址。例如同学们的学号,通常同一届学生的学号,其中前面的部分差别不太大,所以用后面的部分来构造散列地址。
-
平方取中法:当无法确定关键字里哪几位的分布相对比较均匀时,可以先求出关键字的平方值,然后按需要取平方值的中间几位作为散列地址。这是因为:计算平方之后的中间几位和关键字中的每一位都相关,所以不同的关键字会以较高的概率产生不同的散列地址。
-
取随机数法:使用一个随机函数,取关键字的随机值作为散列地址,这种方式通常用于关键字长度不同的场合。
-
除留取余法:取关键字被某个不大于散列表的表长 的数 除后所得的余数 为散列地址。这种方式也可以在用过其他方法后再使用。该函数对 的选择很重要,一般取素数或者直接用 。
哈希表的冲突
存在一个问题,可能存在两个 ,分别为 ,。使得 ,即此时产生了冲突。
解决冲突有很多种方法:
-
开放地址法(也叫开放寻址法):实际上就是当需要存储值时,对Key哈希之后,发现这个地址已经有值了,这时该怎么办?不能放在这个地址,不然之前的映射会被覆盖。这时对计算出来的地址进行一个探测再哈希,比如往后移动一个地址,如果没人占用,就用这个地址。如果超过最大长度,则可以对总长度取余。这里移动的地址是产生冲突时的增列序量。
-
再哈希法:在产生冲突之后,使用关键字的其他部分继续计算地址,如果还是有冲突,则继续使用其他部分再计算地址。这种方式的缺点是时间增加了。即我们可以用另一个函数 去计算 和 ,并使得 ,从而找到新的位置(这种方式与查字典很像,我们查找 ant 和 and 两个单词,首字母 a 发生了冲突,就接着继续哈希第二个字母)。
-
链地址法:链地址法其实就是对 通过哈希之后落在同一个地址上的值,做一个链表。即对于 和 ,如果这两个产生了冲突,我们让这两个 都接在 的链表上。在很多高级语言的实现当中,也是使用这种方式处理冲突的,我们一般着重使用这种方式。
实现过程中,我们一般采用后一种。假设我们使用第二种方法解决冲突。
对于插入元素 :计算 ,把 插入到 链表中。
对于查询元素 :计算 ,如果 为空,说明 不存在。否则遍历 链表,寻找 。
哈希表的代码实现
我们以结点 为整数做代码介绍,此时可直接将 的值作为关键字。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
2020-04-15 程序时间记录
2020-04-15 希尔排序