构建哈希表常见的解决冲突的方法:拉链法和线性探测法
影响哈希查找效率的一个重要因素是哈希函数本身。当两个不同的数据元素的哈希值相同时,就会发生冲突。为减少发生冲突的可能性,哈希函数应该将数据尽可能分散地映射到哈希表的每一个表项中。解决冲突的方法有以下两种:
1、开放定址法:
所谓开放定址法,即由关键码得到的哈希地址一旦产生了冲突,也就是说,该地址已经存放了数据元素。我们需要寻找下一个空的哈希地址,只要哈希表足够大,空的哈希地址总能找到,并将数据元素存入。常用的找空哈希地址方法有下列三种。
- ① 线性探测法
Hi = ( Hash(key) + di ) % m
其中,Hash(key)为哈希函数,m为哈希表长度, d为增量序列1,2,…,m-1,且 = i 。
设关键码集为 {47,7,29,11,16,92,22,8,3},哈希表表长为11,Hash(key)=key % 11,用线性探测法处理冲突,构造哈希表如下表所示:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
11 | 22 | 47 | 92 | 16 | 3 | 7 | 29 | 8 |
47,7,11,16,92均是由哈希函数得到的没有冲突的哈希地址,因而是直接存入的。Hash(29)=7,哈希地址上冲突,需寻找下一个空的哈希地址:
H1 = ( Hash(29) + 1 ) % 11 = 8,哈希地址8为空,所以将29存入。
另外,22,8同样在哈希地址上有冲突,也是由 找到空的哈希地址的;而Hash(3)=3,哈希地址上冲突,因为:
H1 = ( Hash(3) + 1 ) % 11 = 4,仍然冲突
H2 = ( Hash(3) + 2 ) % 11 = 5,仍然冲突
H3 = ( Hash(3) + 3 ) % 11 = 6,找到空的哈希地址,存入。
线性探测法可能使第i个哈希地址的同义词存入第i+1个哈希地址,这样本应存入第i+1个哈希地址的元素变成了第i+2个哈希地址的同义词……因此,可能出现很多元素在相邻的哈希地址上“堆积”起来,大大降低了查找效率。为此,可采用二次探测法,或再哈希函数探测法,以改善“堆积”问题。
- ② 二次探测法
Hi = ( Hash(key) + di ) % m
其中,Hash(key)为哈希函数,m为哈希表长度, d为增量序列,,,,…,,且
仍对前面例子的关键码序列{47,7,29,11,16,92,22,8,3},用二次探测法处理冲突,构造哈希表如下表所示。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
11 | 22 | 3 | 92 | 16 | 7 | 29 | 8 |
与关键码寻找空的哈希地址只有3这个关键码不同,Hash(3)=3,哈希地址上冲突,由[图片上传失败...(image-c7d04d-1609756554547)]
H1 = ( Hash(3) + ) % 11 = 4,仍然冲突
H2 = ( Hash(3) - ) % 11 = 2,找到空的哈希地址,存入。
- ③ 再哈希法
Hi = ( Hash(key) + i * ReHash(key) ) % m
其中,Hash(key),ReHash(key)是两个哈希函数,m为哈希表长度。
再哈希法,先用第一个函数Hash(key)对关键码计算哈希地址,一旦产生地址冲突,再用第二个函数ReHash(key)确定移动的步长因子,最后,通过步长因子序列由探测函数寻找空的哈希地址。
比如,Hash(key)=a时产生地址冲突,就计算ReHash(key)=b,则探测的地址序列为:
H1 = ( a + 1 *b ) % m,仍然冲突
H2 = ( a + 2 *b ) % m,仍然冲突
H3 = ( a + 3 *b ) % m,找到空的哈希地址,存入。
2、链地址法:
将哈希值相同的数据元素存放在一个链表中,在查找哈希表的过程中,当查找到这个链表时,必须采用线性查找方法。这样的好处是,不怕冲突多;缺点是降低了散列结构的随机存储性能。本质是用单链表结构辅助散列结构的不足。
链地址法又称拉链法,设哈希函数得到的哈希地址域在区间[0,m-1]上,以每个哈希地址作为一个指针,指向一个链,即分配指针数组:
ElemType eptr[m];
建立m个空链表,由哈希函数对关键码转换后,映射到同一哈希地址i的同义词均加入eptr[i]指向的链表中。
对关键码序列为 {47,7,29,11,16,92,22,8,3,50,37,89,94,21},哈希函数为Hash(key)=key % 11,用拉链法处理冲突,建表下所示。
3、建立一个公共溢出区
设哈希函数产生的哈希地址集为[0,m-1],则分配两个表:
一个基本表ElemType base_tbl[m];每个单元只能存放一个元素。
一个溢出表ElemType over_tbl[k];只要关键码对应的哈希地址在基本表上产生冲突,则所有这样的元素一律存入该表中。
查找时,对给定值kx通过哈希函数计算出哈希地址i,先与基本表的base_tbl[i]单元比较,若相等,查找成功;否则,再到溢出表中进行查找。