文章目录
一. 哈希思想
在顺序结构
以及树结构
中,元素关键码和其存储位置没有对应的关系
,因此在查找一共元素时,需要进行多次关键码的比较,顺序查找时间复杂度为O(N),树结构中的平衡树,查找效率可以达到树的高度,也就是O(log2 N),搜索效率取决于搜索过程元素的比较次数
而理想的搜索方式是:可以不经过任何比较,一次直接从表中获取到要查找的元素
,当然这是理想化的
但是如果构建一种存储结构,关键码和元素的存储位置有一定的映射关系
,那么查找是否就会变得更加的高效呢。这就是哈希思想
,映射关系的确立往往通过某种函数,也就是哈希函数
插入元素
时:根据待插入元素的关键码,以哈希函数
计算出该元素的存储位置并按此位置进行存放
搜索元素
:对元素的关键码进行同样的计算,把求得的函数数值当作元素的存储位置,在结构中按此位置取元素进行比较,若关键码相等,则搜索成功
这就是哈希(散列)方法
,哈希函数又称散列函数
,构造的结构称为哈希表(散列表)
二. 哈希冲突
我们首先举例一个简单的哈希表
一个数组:{1 , 77 , 6 , 14 , 5 , 9}
那么根据哈希函数,数组各元素在hash中的映射关系如下图
可以看到,我们在插入数据时,并没有进行元素的比较
,所以这样插入效率比较高
同时,搜索也使用哈希函数进行搜索,没有进行元素的比较,搜索效率也就更高
但如果我们再插入一个44,那么该元素应该存储在哪个位置呢?
按照当前的哈希函数,44%10=4,那么就应该放在下标为4的位置,但是该位置已经被先前的14占用了。
不同的两个数据元素的关键码,换算的哈希函数的值是相同的,也就是对应的下标相同,但是一个位置只能存储一个元素。不同关键码通过哈希函数计算出相同的哈希地址,这就是哈希冲突(哈希碰撞)
三. 哈希函数
哈希冲突产生的原因,就是哈希函数设计不够合理,导致不同关键码通过哈希函数,计算出的哈希值相同。
那么接下来就来介绍一些常见的哈希函数
(1). 直接定址法–(常用)
直接定址法,是取关键字的某个线性函数为散列地址:Hash(key) = A * key + B
我们最常见的例子:比如26个字母的映射
因为字母有对应的ASCII码值,a对应97,b对应98…
哈希函数为:Hash(key) = 1 * key - 97
所以我们可以开一个大小为26的数组,
0号下标对应a,即1 * 97 - 97 = 0
1号下标对应b,即1 * 98 - 97 = 1
优点:简单,均匀
缺点:需要事先知道关键字的分布情况
使用场景:适合查找比较小且连续的情况
(2). 除留余数法–(常用)
除留余数法就是上述哈希冲突演示的哈希函数
设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数
按照哈希函数:Hash(key) = key % p (p<=m)
(3). 平方取中法–(了解)
假设关键字为1234,对它的平方为1522756,抽取中间的3位数
,227作为哈希地址
再比如4321,对它的平方为18671041,抽取中间的3位数,671(或710)作为哈希地址
该方法比较适合:不知道关键字的分布,且位数又不是很大的情况
(4). 折叠法–(了解)
折叠法是将关键字从左到右分割成位数相等的几部分(最后一部分可以短一些),然后将这几部分叠加求和,并按散列表表长,取后几位作为散列地址
比如10个元素
第一个元素:123456,每3个位数分为一组,叠加求和,123+456=579,再579%=10 - > 9
第二个元素:654321,每3个位数分为一组,叠加求和,654+321=975,再975%=10 -> 5
折叠法适合事先不需要知道关键字的分布,适合关键字位数比较多的情况
(5). 随机数法–(了解)
选择一个随机函数,取关键字的随机函数值为它的哈希地址
即Hash(key) = random(key)
,random为随机数函数
通常用于关键字长度不等时
(6). 数学分析法–(了解)
设有 n 个 d 位数,每一位可能有 r 种不同的符号,这 r 种不同的符号在各位上出现的频率不一定相同,可能在某些位上分布比较均匀,每种符号出现的机会均等,在某些位上分布不均匀,只有某几种符号经常出现。可根据散列表的大小,选择其中各种符号分布均匀的若干位,即重复少的若干位,作为散列地址
数学分析法,通常适合处理关键字位数比较大的情况,如果事先知道关键字的分布,且关键字的若干位分布较均匀,重复较少
。
结束语
哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但是哈希冲突无法避免
,只能尽可能的减少出现的次数
本篇内容到此就结束了,感谢你的阅读!
如果有补充或者纠正的地方,欢迎评论区补充,纠错。如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。