一、哈希表的基本概念
哈希表(Hash Table)又称散列表,是除顺序表存储结构、链接表存储结构和索引表存储结构之外的又一种存储线性表的存储结构。
哈希表存储的基本思路是:设要存储的对象个数为n,设置一个长度为m(m≥n)的连续内存单元。
以线性表中每个对象的关键字ki(0≤i≤n-1)为自变量,通过一个称为哈希函数的函数h(ki),把ki映射为内存单元的地址(或称下标)h(ki),并把该对象存储在这个内存单元中。
h(ki)也称为哈希地址(又称散列地址)。把如此构造的线性表存储结构称为哈希表。
哈希冲突:对于两个关键字ki和kj(i≠j),有ki≠kj(i≠j),但h(ki)=h(kj)。
通常把这种具有不同关键字而具有相同哈希地址的对象称做“同义词”,由同义词引起的冲突称作同义词冲突。
在哈希表存储结构的存储中,同义词冲突是很难避免的,除非关键字的变化区间小于等于哈希地址的变化区间,而这种情况当关键字取值不连续时是非常浪费存储空间的。通常的实际情况是关键字的取值区间远大于哈希地址的变化区间。
二、哈希函数构造方法
构造哈希函数的目标是使得到的哈希地址尽可能均匀地分布在n个连续内存单元地址上,同时使计算过程尽可能简单以达到尽可能高的时间效率。
1. 直接定址法
直接定址法是以关键字k本身或关键字加上某个数值常量c作为哈希地址的方法。
直接定址法的哈希函数h(k)为:h(k)=k+c
这种哈希函数计算简单,并且不可能有冲突发生。
当关键字的分布基本连续时,可用直接定址法的哈希函数;否则,若关键字分布不连续将造成内存单元的大量浪费。
2. 除留余数法
除留余数法是用关键字k除以某个不大于哈希表长度m的数p所得的余数作为哈希地址的方法。
除留余数法的哈希函数h(k)为:h(k)=k mod p (mod为求余运算,p≤m)
p最好是质数(素数)。
3. 数字分析法
该方法是提取关键字中取值较均匀的数字位作为哈希地址的方法。它适合于所有关键字值都已知的情况,并需要对关键字中每一位的取值分布情况进行分析。
例如,对于一组关键字:{92317602,92326875,92739628,92343634,92706816,92774638,92381262,92394220}
通过分析可知,每个关键字从左到右的第1、2、3位和第6位取值较集中,不宜作为哈希函数,剩余的第4、5、7和8位取值较分散,可根据实际需要取其中的若干位作为哈希地址。
若取最后两位作为哈希地址,则哈希地址的集合为{2,75,28,34,16,38,62,20}。
三、哈希冲突解决方法
在哈希表中,虽然冲突很难避免,但发生冲突的可能性却有大有小。这主要与三个因素有关:
1.与装填因子有关
所谓装填因子α是指哈希表中已存入的元素数n与哈希地址空间大小m的比值。
即α=n/m, α越小,冲突的可能性就越小; α越大(最大可取1),冲突的可能性就越大。
这很容易理解,因为α越小,哈希表中空闲单元的比例就越大,所以待插入元素同已插入的元素发生冲突的可能性就越小;
反之, α越大,哈希表中空闲单元的比例就越小,所以待插入元素同已插入的元素冲突的可能性就越大;
另一方面, α越小,存储空间的利用率就越低;反之,存储空间的利用率也就越高。
为了既兼顾减少冲突的发生,又兼顾提高存储空间的利用率这两个方面,通常使最终的控制在0.6~0.9的范围内。
2.与所采用的哈希函数有关
若哈希函数选择得当,就可使哈希地址尽可能均匀地分布在哈希地址空间上,从而减少冲突的发生;否则,若哈希函数选择不当,就可能使哈希地址集中于某些区域,从而加大冲突的发生。
3.与解决冲突的哈希冲突函数有关。
哈希冲突函数选择的好坏也将减少或增加发生冲突的可能性。
下面是解决方法:
1. 开放定址法
开放定址法是一类以发生冲突的哈希地址为自变量,通过某种哈希冲突函数得到一个新的空闲的哈希地址的方法。
(1)线性探测法
线性探测法是从发生冲突的地址(设为d)开始,依次探测d的下一个地址(当到达下标为m-1的哈希表表尾时,下一个探测的地址是表首地址0),直到找到一个空闲单元为止(当m≥n时一定能找到一个空闲单元)。
线性探测法的数学递推描述公式为:
d0=h(k)
di=(di-1+1) mod m (1≤i≤m-1)
(2)平方探测法
设发生冲突的地址为d,则平方探测法的探测序列为:d±12,d±22,…。
平方探测法的数学描述公式为:
d0=h(k)
di=(d0± i2) mod m (1≤i≤m-1)
平方探测法是一种较好的处理冲突的方法,可以避免出现堆积问题。
它的缺点是不能探测到哈希表上的所有单元,但至少能探测到一半单元。
例1,假设哈希表长度m=13,采用除留余数法哈希函数建立如下关键字集合的哈希表:
{16,74,60,43,54,90,46,31,29,88,77}。
解:n=11,m=13,
除留余数法的哈希函数为:h(k)=k mod p
p应为小于等于m的素数,假设p取值13。
则有:h(16)=3,h(74)=9,h(60)=8,h(43)=4,h(54)=2,h(90)=12,h(46)=7,h(31)=5,
h(29)=3 发生冲突
d0=h(29)=3,d1=(3+1) mod 13=4 仍有冲突
d2=(4+1) mod 13=5 仍有冲突
d3=(5+1) mod 13=6 解决冲突
h(88)=10
h(77)=12 发生冲突
d0=h(77)==12,d1=(12+1) mod 13=0 解决冲突
2. 拉链法
拉链法是把所有的同义词用单链表链接起来的方法。
在这种方法中,哈希表每个单元中存放的不再是记录本身,而是相应同义词单链表的头指针。
由于单链表中可插入任意多个节点,所以此时装填因子α根据同义词的多少既可以设定为大于1,也可以设定为小于或等于1,通常取α=1。
对上例构造的哈希表采用拉链法解决冲突。
解:采用拉链法解决冲突建立的链表如下图所示