哈希表一

来源:互联网 发布:linux vi 编辑 编辑:程序博客网 时间:2024/06/05 20:22

一、定义

哈希表(散列表)通过将关键码映射到表中的某个位置上来存储元素,然后根据关键码来访问元素。

具体来说,就是在关键字 k 和元素的存储位置 p 之间建立一个对应关系 f ,使得 p=f(k) , f 称为哈希函数 。创建哈希表时,把关键字为 k 的元素 直接存入地址为 f(k) 的单元 ;以后当查找关键字为 k 的元素时,再利用哈希函数计算出该元素的存储位置 p=f(k) ,从而达到按关键 字直接存取元素的目的。

二、哈希函数

构造哈希函数的原则是:

1)函数本身便于计算;
2)计算出来的地址分布均匀,即对任一关键字k,f(k)对应不同的地址的概率相等.

选择不同方法需考虑的因素:

1)计算哈希函数所需时间;
2)关键字的长度;
3)哈希表大小;
4)关键字分布情况;
5)查找频率

1、数字分析法

如果事先知道关键字集合,并且每个关键字的位数比哈希表的地址码位数多时,可以从关键字中选出分布较均匀的若干位,构成哈希地址。

2、平方取中法

当无法确定关键字中哪几位分布较均匀时,可以先求出关键字的平方值,然后按需要取平方值的中间几位作为哈希地址。这是因为:平方后中间几位和关键字中每一位都相关,故不同关键字会以较高的概率产生不同的哈希地址。

3、分段叠加法

这种方法是按哈希表地址位数将关键字分成位数相等的几部分(最后一部分可以较短),然后将这几部分相加,舍弃最高进位后的结果就是该关键字的哈希地址。具体方法有折叠法与移位法。移位法是将分割后的每部分低位对齐相加,折叠法是从一端向另一端沿分割界来回折叠(奇数段为正序,偶数段为倒序),然后将各段相加。

4、除留取余法

假设哈希表长为m,p为小于等于m的最大素数,则哈希函数为
hash(k)=k % p ,其中%为模p取余运算。

5、伪随机数法

采用一个伪随机函数做哈希函数,即h(key)=random(key)。

6、直接定制法

取关键字的某个线性函数作为散列函数,Hash(key)=A*key+B;
但是这种方法有很大的缺陷,就是当关键码比较分散时,hash表的所浪费的空间是非常大的。

三、解决碰撞方法

使用哈希表会带来一个问题:可能有不同的元素映射到同一个位置,这便是碰撞问题。解决碰撞问题的方法有如下几种:

1、开放定址法

 用开放定址法解决冲突的做法是:当冲突发生时,使用某种探测技术在散列表中形成一个探查(测)序列。沿此序列逐个单元地查找,直到找到给定 的关键字,或者碰到一个开放的地址(即该地址单元为空)为止。

1)线性探测

hash(key)+1,hash(key)+2, .... hash(key)+i

在线性探测中,冲突时通过顺序往后扫描数组(如果到达尾端,就绕道头部继续寻找),直到找到空的位置。

这个方法的缺点就是,会有大量元素扎堆,形成“主集团”,导致局部大规模发生冲突。

2)线性补偿探测

将线性探测的步长从 1 改为 Q ,Q是与m互质的,这样便能探测到哈希表中左右单元。

3)二次探测

hash(key)+1^2,hash(key)+2^2, .... hash(key)+i^2

二次探测,其命名来源是其解决碰撞问题的方程式是二次的F(i)=i^2.它不像线性探测那样顺序往后找,而是平方,平方的,这样就解决了扎堆问题。

4)随机探测

将线性探测的步长从常数改为随机数,即令: j = (j + RN) % m ,其中 RN 是一个随机数。在实际程序中应预先用随机数发生器产生一个随机序列,将此序列作为依次探测的步长。这样就能使不同的关键字具有不同的探测次序,从而可以避 免或减少堆聚。基于与线性探测法相同的理由,在线性补偿探测法和随机探测法中,删除一个记录后也要打上删除标记。

2、再哈希法

这种方法是同时构造多个不同的哈希函数。当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。

3、链地址法

在每一个表格中维护一个list.

拉链法优点:

1)解决冲突简单,无堆积现象,因此平均查找时间短;
2)节点空间动态申请,适合建表前无法确定表长的情况;
3)删除节点方便。链地址法只要简单的删除链表上的相应节点即可;而开放地址法的删除操作,只能在被删除的节点上做删除标记,而不做真正的删除节点。

拉链法缺点:

指针需要额外空间,当节点规模较小时,开放地址法更节省空间。

4、建立公共溢出区

这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表

0 0
原创粉丝点击