一、哈希表
哈希表,也称散列表,英文名称Hash Table。是根据关键码值(Key value)而直接进行访问的数据结构,也就是说通过把关键码值映射到表中一个位置来访问记录以便加快查找的速度。这个映射的函数叫做散列函数,存放记录的数组叫做散列表。
可以定义为:给定表Table,存在函数F(key),对任意给定的关键字key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表Table为哈希(Hash)表,函数F(key)为哈希(Hash)函数。
二、哈希函数
即哈希函数,也称散列函数,意义:根据关键码值key计算出对应记录的存储位置,position = F(key)。
散列函数满足以下条件:
(1)对输入值运算,得到一个固定长度的摘要(Hash value);
(2)不同的输入值可能产生相同的输出值;
比如:F(x) = x mod 12、F(x) = (x | 0×0000FFFF) XOR (x >> 16)等就可以认为是一个散列函数。同时我们还希望散列函数能满足以下几点:
(1)散列的输出值尽量接近均匀分布;
(2)自变量X的微波变化可以使F(x)发生非常大的变化,即“雪崩效应”;
三、构造Hash函数的方法
(1)直接定址法:哈希函数为关键字的线性函数,H(key) = key 或者 H(key) = a * key + b
此法适于:地址集合的大小 == 关键字集合的大小,其中a和b为常数。
(2)数字分析法:假设关键字集合中的每个关键字都是由 s 位数字组成 (u1, u2, …, us),分析关键字集中的全体,并从中提取分布均匀的若干位或它们的组合作为地址
此法适于:能预先估计出全体关键字的每一位上各种数字出现的频度
(3)平方取中法: 以关键字的平方值的中间几位作为存储地址。求“关键字的平方值” 的目的是“扩大差别” ,同时平方值的中间各位又能受到整个关键字中各位的影响
此法适于:关键字中的每一位都有某些数字重复出现频度很高的现象
(4)折叠法:将关键字分割成若干部分,然后取它们的叠加和为哈希地址。两种叠加处理的方法:移位叠加:将分割后的几部分低位对齐相加;间界叠加:从一端沿分割界来回折叠,然后对齐相加
此法适于:关键字的数字位数特别多
(5)除留余数法:设定哈希函数为:H(key) = key MOD p ( p≤m ),其中, m为表长,p 为不大于 m 的素数,或 是不含 20 以下的质因子
(6)随机数法:设定哈希函数为:H(key) = Random(key)其中,Random 为伪随机函数
此法适于:对长度不等的关键字构造哈希函数
实际造表时,不论采用哪种哈希函数构建哈希表,总的原则是使产生冲突的可能性尽可能地小
四、哈希表的冲突处理
由上面的方法可以看到,不论哪种方面总会产生冲突,处理冲突的实际含义是为冲突的关键字寻找下一个哈希地址。
1、开放地址法
这个方法的基本思想是:当发生地址冲突时,按照某种方法继续探测哈希表中的其他存储单元,直到找到空位置为止。这个过程可用下式描述: H i ( key ) = ( H ( key )+ d i ) mod m ( i = 1,2,…… , k ( k ≤ m – 1)) ,其中: H ( key ) 为关键字 key 的直接哈希地址, m 为哈希表的长度, di 为每次再探测时的地址增量。
采用这种方法时,首先计算出元素的直接哈希地址 H ( key ) ,如果该存储单元已被其他元素占用,则继续查看地址为 H ( key ) + d 2 的存储单元,如此重复直至找到某个存储单元为空时,将关键字为 key 的数据元素存放到该单元。
增量 d 可以有不同的取法,并根据其取法有不同的称呼:
(1) d i = 1 , 2 , 3 , ……线性探测再散列;
(2) d i = 1^2 ,- 1^2 , 2^2 ,- 2^2 , k^2, -k^2……二次探测再散列;
(3) d i = 伪随机序列 伪随机再散列,随机探测的基本思想是:将线性探测的步长从常数改为随机数,即令: j = (j + RN) % m ,其中 RN 是一个随机数。在实际程序中应预先用随机数发生器产生一个随机序列,将此序列作为依次探测的步长。这样就能使不同的关键字具有不同的探测次序,从而可以避 免或减少堆聚。基于与线性探测法相同的理由,在线性补偿探测法和随机探测法中,删除一个记录后也要打上删除标记。;
例:设有哈希函数 H ( key ) = key mod 7 ,哈希表的地址空间为 0 ~ 6 ,对关键字序列(32 , 13, 49 , 55, 22 , 38, 21 )按线性探测再散列和二次探测再散列的方法分别构造哈希表。
解:( 1 )线性探测再散列:
32 % 7 = 4 ;
13 % 7 = 6 ;
49 % 7 = 0 ;
55 % 7 = 6 发生冲突,下一个存储地址(6 + 1) % 7 = 0 ,仍然发生冲突,再下一个存储地址:(6 + 2) % 7 = 1 未发生冲突,可以存入;
22 % 7 = 1 发生冲突,下一个存储地址是:( 1 + 1 )% 7 = 2 未发生冲突;
38 % 7 = 3 ;
21 % 7 = 0 发生冲突,按照上面方法继续探测直至空间 5 ,不发生冲突,所得到的哈希表对应存储位置:
下标: 0 1 2 3 4 5 6 49 55 22 38 32 21 13
( 2 )二次探测再散列:
下标: 0 1 2 3 4 5 6 49 22 21 38 32 55 13
注意:对于利用开放地址法处理冲突所产生的哈希表中删除一个元素时需要谨慎,不能直接地删除,因为这样将会截断其他具有相同哈希地址的元素的查找地址,所以,通常采用设定一个特殊的标志以示该元素已被删除。
2、拉链法
拉链法解决冲突的做法是:将所有关键字为同义词的结点链接在同一个单链表中。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数 组T[0..m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。在拉链法中,装填因子α可以大于 1,但一般均取α≤1。
3、再哈希法
这种方法的思想是同时构造多个不同的哈希函数:H1=RH1(key) i=1,2,3...,k。当哈希地址Hi=RH1(key)发生冲突时再计算Hi=RH2(key)...,直到冲突不再产生。这种方法不易产生聚焦,但增加了计算时间。