哈希表——概念

在之前讨论的各种数据结构中,数据元素的存放位置是人为给定的,和数据元素本身没有直接关系。这种情况下,存取一个元素只有按地址直接存取和遍历查找两种方法。

顺序存储结构和链式存储结构是两种基本的存储结构,哈希表是一种特殊类型的存储结构,它是一种数据元素及其在内存中的位置之间存在某种函数关系的存储结构,如果构造合适,时间效率非常高。

哈希表主要是构造一个映射函数,该函数以数据元素为自变量,函数值即为数据元素在内存中的存储位置。这样的映射函数称为哈希函数h(x)。

哈希表的构造方法:

设要存储的数据元素个数为n,设置一个长度为m(m>=n)的连续内存单元(即数组),分别以每个数据元素的关键字Ki(0<=i<=n-1)为自变量,以哈希函数h(Ki)值为该数据元素在数组中的下标值存储该数据元素。哈希函数也称哈希地址。

存在的问题:

对于两个数据元素的关键字(主关键字)Ki和Kj,i≠j时,有Ki≠Kj,但h(Ki)=h(Kj)。也就是两个不一样的数据元素,经过哈希函数映射得到了一样的地址,这种现象称作哈希冲突。通常把这种具有不同关键字而具有相同哈希地址的哈希冲突称作同义词冲突。

冲突解决办法:

通过设计哈希冲突函数产生一个新的哈希地址,使不发生冲突。哈希冲突函数通常是一组,因为哈希冲突函数产生的地址还是有可能再次发生冲突,此时,再用下一个冲突函数得到新地址,一直到不冲突为止。

哈希表中查找:

通过关键字K,以建立哈希表时使用的相同哈希函数为映射得到地址,比较改地址中的Ki是否等于K,若相等查找成功,否则,以同样的哈希冲突函数为映射得到新地址中的元素Kj,再作比较···直到成功查找到的元素等于K为止,或者,查找完m次未查找到失败为止。

构造哈希表的2个关键问题:

1、如何设计一个好的哈希函数,使得尽量避免哈希冲突。

2、哈希冲突发生后,如何解决哈希冲突。

与哈希冲突相关的3个主要因素:

1、装填因子α:α=n/m,n为要存入的数据元素个数,m为数组长度。α越小,冲突的可能性就越小,反之,越大。但是,α越小,哈希表中空闲单元的比例就越大,存储单元的利用率就越低,所以,通常把α控制在0.6~0.9的范围内。

2、所采用的哈希函数:选择得当,可以使哈希地址尽可能均匀地分布在哈希表上,从而减少冲突发生。否则,会使哈希地址集中于哈希表的某些区域,加大冲突发生可能性。

3、所采用的哈希冲突函数:选择好坏影响随后发生冲突的可能性。

哈希函数常用构造方法如下:

注明:(1)设要存放的数据元素有n个,数组长度为m。(2)只讨论整数类型关键字的哈希函数设计。

1、除留余数法h(K)=K mod m

以得到的余数作为哈希地址,这种方法计算简单,适用范围广,并且映射到m个数组元素下标的概率相等,从而减少冲突发生。

理论研究表明,使用除留余数法时,数组长度m取素数的效果最好。根据前面装填因子α的取值范围,可得出m最好取1.1n~1.7n之间的一个素数。

2、直接定址法h(K)=K+C

C为某个设定的数值常量,该法计算简单,并且不可能发生冲突,但可能造成内存单元的大量浪费。

3、数字分析法

该法是取数据元素关键字中某些取值较均匀的数字位构造哈希函数,它只适用于所有关键字值已知的情况。当已知所有关键字值,可对关键字中每一位的取值分布情况做出分析,从而可以把一个很大的关键字取值区间转化为一个较小的关键字取值区间。

如关键字:K1=61 317 602 K2=61 326 875 K3=62 739 628 K4=61 343 634 K5=62 706 816 K6=62 774 638 K7=61 381 262 K8=61 394 220

分析以上8个关键字,从左到右第1,2,3,6位取值比较集中,不宜作为哈希地址,剩余的第4,5,7,8位取值较均匀,可选取其中的两位作为哈希地址,比如选后两位则这8个关键字的哈希地址分别为:2,75,28,34,16,38,62,20。

哈希冲突解决办法:

1、开放定址法

这是一类以发生哈希冲突的哈希地址为自变量,通过某种哈希冲突函数得到一个新的空闲的哈希地址的方法,这里用到的哈希冲突函数通常也是一组。

在开放定址法中,哈希表中的空闲单元(假设其数组下标为d)不仅允许哈希地址为d的同义词数据元素使用,还允许发生冲突的其他数据元素使用,因为这些元素的哈希地址不为d,所以称为非同义词关键字。至于哈希表的一个地址中存放怎样的数据元素,要看谁先占用它,这和构造哈希表时的数据元素排列次序有关。

在开放定址法中,若先是因为解决某哈希地址v发生的同义词冲突而占用了下标为d的位置,此时再有一个数据元素本身哈希地址为d,但d位置已经被占用,这种冲突即为非同义词冲突。

开放定址法常用的方法如下:

(1)线性探查法:以发生哈希冲突的地址d开始,依次探查d的下一个地址(当到达地址m-1,下一个探查地址为0),直到查找到一个空闲单元为止,当m>=n时一定能找到一个空闲单元。递推公式如下:


线性探查法容易产生堆积问题,即大量的同义词冲突解决占用了本该属于后续元素的哈希地址。

(2)平方探查法:以发生哈希冲突的地址d开始,递推公式如下:


由于该法的探查跨步较大,所以一定程度上可避免出现堆积问题。

(3)伪随机数法:以发生哈希冲突的地址d开始,递推公式如下:


由于该法的探查跨步是随机的,所以也可以一定程度避免出现堆积问题。

2、链表法

基本思想:若没有发生冲突,则直接在该地址保存该数据元素,若发生冲突,则把发生冲突的数据元素保存在另外的单链表中。

常用方法有2种,(1)为发生冲突的不同的同义词建立不同的单链表;(2)为所有发生冲突的数据元素建立一个单链表。

猜你喜欢

转载自blog.csdn.net/sinat_32561655/article/details/71928145