一、什么是hash、哈希函数、哈希表
- Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
- 哈希函数
对比二叉排序树 二叉平衡树 红黑树 B B+树,它们的查找都是先从根节点进行查找,从节点取出数据或索引与查找值进行比较。那么,有没有一种函数H,根据这个函数和查找关键字key,可以直接确定查找值所在位置,而不需要一个个比较。这样就“预先知道”key所在的位置,直接找到数据,提升效率。
即
地址index=H(key)
- 哈希表是根据设定的哈希函数H(key)和处理冲突方法将一组关键字映射到一个有限的地址区间上,并以关键字在地址区间中的映象作为记录在表中的存储位置,这种表称为哈希表或散列,所得存储位置称为哈希地址或散列地址。作为线性数据结构与表格和队列等相比,哈希表无疑是查找速度比较快的一种。
简单理解:hash函数就是根据key计算出应该存储地址的位置,而哈希表是基于哈希函数建立的一种查找表
- 哈希(Hash)算法,即散列函数。它是一种单向密码体制,即它是一个从明文到密文的不可逆的映射,只有加密过程,没有解密过程。同时,哈希函数可以将任意长度的输入经过变化以后得到固定长度的输出。哈希函数的这种单向特征和输出数据长度固定的特征使得它可以生成消息或者数据。
二、常用哈希函数的构造方法
- 直接定制法
哈希函数为关键字的线性函数如 H(key)=a*key+b
这种构造方法比较简便,均匀,但是有很大限制,仅限于地址大小=关键字集合的情况
- 数字分析法
假设关键字集合中的每个关键字key都是由s位数字组成(k 1 , k 2 , … … , k n ),分析key中的全体数据,并从中提取分布均匀的若干位或他们的组合构成全体
- 平方取中法
如果关键字的每一位都有某些数字重复出现频率很高的现象,可以先求关键字的平方值,通过平方扩大差异,而后取中间数位作为最终存储地址。
- 折叠法
如果数字的位数很多,可以将数字分割为几个部分,取他们的叠加和作为hash地址
使用举例。
比如key=123 456 789
我们可以存储在61524,取末三位,存在524的位置
该方法适用于数字位数较多且事先不知道数据分布的情况
- 除留余数法(用的较多)
H(key)=key MOD p (p<=m m为表长)
很明显,如何选取p是个关键问题。
比如我们存储3 6 9,那么p就不能取3
因为 3 MOD 3 == 6 MOD 3 == 9 MOD 3
p应为不大于m的质数或是不含20以下的质因子的合数,这样可以减少地址的重复(冲突)
三、哈希碰撞(hash冲突)及处理
在计算hash地址的过程中会出现对于不同的关键字出现相同的哈希地址的情况,即key1 ≠ key2,但是f(key1) = f(key2),这种情况就是Hash 冲突。具有相同关键字的key1和key2称之为同义词。
通过优化哈希函数可以减少这种冲突的情况(如:均衡哈希函数),但是在通用条件下,考虑到于表格的长度有限及关键值(数据)的无限,这种冲突是不可避免的,所以就需要处理冲突。
冲突处理
冲突处理分为以下四种方式:
- 开放地址
- 再哈希
- 链地址
- 建立公共溢出区
其中开放地址又分为:
- 线性探测再散列
- 二次探测再散列
- 伪随机探测再散列
1.开放地址
开放地址法处理冲突的基本原则就是出现冲突后按照一定算法查找一个空位置存放。公式:
Hi为计算出的地址,H(key)为哈希函数,di为增量。其中di的三种获取方式既是上面提到的开放地址法的三种分类(线性探测再散列、二次探测再散列、伪随机探测再散列)。
- 线性探测再散列,即依次向后查找
- 二次探测再散列,即依次向前后查找,增量为1、2、3的二次方。
- 伪随机探测再散列
伪随机,顾名思义就是随机产生一个增量位移。
2.再哈希法
再哈希法,就是出现冲突后采用其他的哈希函数计算,直到不再冲突为止。
,其中RHi为不同的哈希函数。
3.链地址法
链接地址法不同与前两种方法,他是在出现冲突的地方存储一个链表,所有的同义词记录都存在其中。形象点说就行像是在出现冲突的地方直接把后续的值摞上去。例如HashMap,如下图。
4.建立公共溢出区
建立公共溢出区的基本思想是:假设哈希函数的值域是[1,m-1],则设向量HashTable[0...m-1]为基本表,每个分量存放一个记录,另外设向量OverTable[0...v]为溢出表,所有关键字和基本表中关键字为同义词的记录,不管它们由哈希函数得到的哈希地址是什么,一旦发生冲突,都填入溢出表。
四、hash表的查找
查找过程和造表过程一致,假设采用开放定址法处理冲突,则查找过程为:
- 对于给定的key,计算hash地址index = H(key)
- 如果数组arr【index】的值为空 则查找不成功
- 如果数组arr【index】== key 则查找成功
- 否则 使用冲突解决方法求下一个地址,直到arr【index】== key或者 arr【index】==null
五、hash表的查找效率
决定hash表查找的ASL因素:
1)选用的hash函数
2)选用的处理冲突的方法
3)hash表的饱和度,装载因子 α=n/m(n表示实际装载数据长度 m为表长)
一般情况,假设hash函数是均匀的,则在讨论ASL时可以不考虑它的因素
hash表的ASL是处理冲突方法和装载因子的函数
前人已经证明,查找成功时如下结果:
可以看到无论哪个函数,装载因子越大,平均查找长度越大,那么装载因子α越小越好?也不是,就像100的表长只存一个数据,α是小了,但是空间利用率不高啊,这里就是时间空间的取舍问题了。通常情况下,认为α=0.75是时间空间综合利用效率最高的情况。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。