1 哈希表的构建
1. 直接寻址法
取关键字或者关键字的某个线性函数值作为哈希地址,即H(Key)=Key或者H(Key)=a*Key+b(a,b为整数),这种散列函数也叫做自身函数.如果H(Key)的哈希地址上已经有值了,那么就往下一个位置找,知道找到H(Key)的位置没有值了就把元素放进去.
2. 数字分析法
分析一组数据,比如一组员工的出生年月,这时我们发现出生年月的前几位数字一般都相同,因此,出现冲突的概率就会很大,但是我们发现年月日的后几位表示月份和具体日期的数字差别很大,如果利用后面的几位数字来构造散列地址,则冲突的几率则会明显降低.因此数字分析法就是找出数字的规律,尽可能利用这些数据来构造冲突几率较低的散列地址.
3. 平方取中法
取关键字平方后的中间几位作为散列地址.一个数的平方值的中间几位和数的每一位都有关。因此,有平方取中法得到的哈希地址同关键字的每一位都有关,是的哈希地址具有较好的分散性。该方法适用于关键字中的每一位取值都不够分散或者较分散的位数小于哈希地址所需要的位数的情况。
4. 折叠法
折叠法即将关键字分割成位数相同的几部分,最后一部分位数可以不同,然后取这几部分的叠加和(注意:叠加和时去除进位)作为散列地址.数位叠加可以有移位叠加和间界叠加两种方法.移位叠加是将分割后的每一部分的最低位对齐,然后相加;间界叠加是从一端向另一端沿分割界来回折叠,然后对齐相加.
5. 随机数法
选择一个随机数,去关键字的随机值作为散列地址,通常用于关键字长度不同的场合.
6. 除留余数法
取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址.即H(Key)=Key MOD p,p<=m.不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。对p的选择很重要,一般取素数或m,若p选得不好,则很容易产生冲突。一般p取值为表的长度tableSize。
2 哈希表的冲突处理
何为哈希碰撞:
如果有两个数的余数相等,那么我的存储位置就会是在哈希表中的同一个下标之下,我们称这种情况为哈希碰撞。
2.1 开放地址法
开放地址法的基本思想是:有冲突的时候就去寻找下一个空的散列表地址,只要散列表足够大,空的散列地址总能找到,并将数据元素存入
除留余数法
H
i
=
(
\mathrm{H}_{\mathrm{i}}=\left(\right.
Hi=( Hash(key)
+
d
i
)
\left.+\mathrm{d}_{\mathrm{i}}\right)
+di) mod
m
m
m
d
i
d_i
di 为增量序列
线性探测法
线性探测法的基本思想是,假定哈希函数为_H_(key),哈希表的地址区间长度为_m_,并将哈希表看成是一个循环空间,则线性探测法的探测地址序列可表示为
d i \mathrm{d}_{\mathrm{i}} di 为 1 , 2 , … m − 1 1,2, \ldots \mathrm{m}-1 1,2,…m−1
用线性探测法处理冲突,思路清晰,算法简单,但线性探测法很容易产生堆聚现象。所谓堆聚现象,就是存入哈希表的记录在表中练成一片。按照线性探测法处理冲突,如果堆聚的记录越多,则发生冲突时的探测次数越多。
二次探测法
d i d_i di 为 1 2 , − 1 2 , 2 2 , − 2 2 , … , q 2 1^2,-1^2, 2^2,-2^2, \ldots, q^2 12,−12,22,−22,…,q2 二次序列
二次探测能有效避免“聚集”现象,但是不能够探测到哈希表上所有的存储单元,但是至少能够探测到一半。
例题:
设哈希表长为11,哈希函数为Hash (key)=key%11。存在关键码{43,7,29,22,16,92,44,8,19},采用二次探测法处理冲突,建立的hash表为( )
-
A、 -
B、 -
C、 -
D、 其他几项都不对
43,7,29,22,16,92,44,8,19
43%11 = 10 H[10] = 43
7%11 = 7 H[7] = 7
29%11 = 7 H[7] = 29 f发生哈希冲突,那么向右移动一位 H[8] = 29
22%11 = 0 H[0] = 22
16%11 = 5 H[5] = 16
92%11 = 4 H[4] = 92
44%11= 0 H[0] = 44 发生了哈希冲突 ,那么向右移动一位 H[1] = 44
8 %11= 9 H[ 9] = 8
19 %11= 8 H[ 8] =19 发生哈希冲突 ,先向右边移动一位, H[ 9] =19 发生冲突,那么向左移动一位H[ 7] =19 依旧发生冲突,向右边移动两位,H[10] = 19 依旧冲突。向左移动两位,H[ 6] =19
故选择A
2.2链地址法
链地址法也成为拉链法。其基本思路是:将所有具有相同哈希地址的而不同关键字的数据元素连接到同一个单链表中。如果选定的哈希表长度为m,则可将哈希表定义为一个有m个头指针组成的指针数组T[0…m-1],凡是哈希地址为i的数据元素,均以节点的形式插入到T[i]为头指针的单链表中。并且新的元素插入到链表的前端,这不仅因为方便,还因为经常发生这样的事实:新近插入的元素最优可能不久又被访问。
链地址法特点
(1)拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短;
(2)由于拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况;
(3)开放定址法为减少冲突,要求装填因子α较小,故当结点规模较大时会浪费很多空间。而拉链法中可取α≥1,且结点较大时,拉链法中增加的指针域可忽略不计,因此节省空间;
(4)在用拉链法构造的散列表中,删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。而对开放地址法构造的散列表,删除结点不能简单地将被删结点的空间置为空,否则将截断在它之后填人散列表的同义词结点的查找路径。这是因为各种开放地址法中,空地址单元(即开放地址)都是查找失败的条件。因此在用开放地址法处理冲突的散列表上执行删除操作,只能在被删结点上做删除标记,而不能真正删除结点。