哈希表的基本思想:
以结点的关键字k为自变量,通过一个确定的函数关系f,计算出对应的函数值,并把这个值解释为结点的存储地址,最后将结点存入f(k)所指示的存储位置上。查找时再根据要查找的关键字,用同样的函数计算地址,然后从相应的单元中读取。函数f称为哈希函数,f(k)的值称为哈希地址,用于存储结点的数据结构称为哈希表。
几个概念:如果有哈希表的空间大小为m,填入表的结点数为n。则称alpha=n/m为装填因子。
若对于某个哈希函数,关键字k1和k2得到相同的哈希地址,则称该现象为冲突。
例子:
关键字集合:S={abc,def,ghe,cba,bca,egh};
哈希表: char hash[26][4]
我们选取关键字key的第一个字母作为哈希函数的变量,选取哈希函数为f(key)=key[0]-'a'。
哈希地址 | 关键字 |
0 | abc |
1 | bca |
2 | cba |
3 | def |
4 | egh |
5 | |
6 | ghe |
7 | |
8 | |
...... | ..... |
哈希函数的构造
哈希函数的构造是处理哈希表的关键之处,一般的要尽可能使函数简单,尽量减少冲突发生。
1. 平方取中法:
先对关键字的进行平方运算,扩大差别,然后再取关键字的几位或者组合作为哈希地址。
如:关键字(0100,0110,1010,1001)
平方得到:(0010000,0012100,1020100,1002001)
如果哈希表表长为1000,则取中间三位为哈希地址。地址集为(100,121,201,020).
2.折叠法
在关键字位数较多时,也可将关键字分割成位数相同的几段,段的长度取决于哈希表的地址位数,然后叠加求和(舍弃进位)作为哈希地址。
3.除留余数法
选择适当的正整数p,用p取除关键字,所得余数作为哈希地址。
f(key)=key%p
4.基数转换法
把关键看成另一种进制的数后,再把它转换成原来的进制的数,取其中若干位作为散列地址。一般取大于原来的数作为转换的基数,并且两个数要互为素数。
如:十进制的数(210485),看成十三进制的数。再把它转换成十进制的数,有新的十进制数(771932).假设哈希表长度为10000,可以取低四位1932作为哈希地址。
5.随机数法
选择一个随机数,去关键字的随机值作为散列地址,通常用于关键字长度不同的场合。
解决冲突的方法
A.开放地址法
当发生冲突时,使用某种方法在散列表中形成一个探查序列,沿着这个序列逐个单元的查找,直到找到一个空的单元格时将新的结点放入为止,固在造表时应该先把表置空。探查序列的构造有以下三种方法:
1.线性探查法。设表长为m,关键字个数为n。基本思想是:将哈希表看成是一个环形表,若发生冲突的地址是d,则依次探查d+1,d+2,d+3....m-1,0,1...d-1.直至找到一个空的单元为止。
开放地址公式为:
d(i)=(d+i)%m, 1<=i<m,d=f(key).
如,一组关键字(26,36,41,38,44,15,68,12,06,51,25),用线性探查法解决冲突问题。
假设构造时,哈希表为hash[15],用除留余数法构造哈希函数,p=13.(没有特定要求,只是举例子)
关键字 | 26 | 36 | 41 | 38 | 44 | 15 | 68 | 12 | 06 | 51 | 25 | |
余数 | 0 | 10 | 2 | 12 | 5 | 2 | 3 | 12 | 6 | 12 | 12 |
哈希表地址 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |
关键字 | 26 | 25 | 41 | 15 | 68 | 44 | 06 | 36 | 38 | 12 | 51 |
2.二次探查法
原理和线性探查法差不多,每次探查空地址时,按d+(1,-1,2^2,-2^2,3^2,-3^2....).探查.
d'=(d+i^2)%m或者d‘=(d-i^2)%m.
3.随机探查法
采用一个随机数作为地址位移计算下一个单元地址。
d'=(d+random)%m.
B.拉链法
将所有关键字为同地址的结点,链接到同一个单链表中.如上面描述的例子,余数为12的数,以地址12构成链表,相连着。