数据结构 - 哈希表

来源:互联网 发布:手机淘宝首页推荐关闭 编辑:程序博客网 时间:2024/06/05 11:43

源自:http://blog.csdn.net/ncepustrong/article/details/8448187


一、哈希表

    哈希表又称散列表。哈希表存储的基本思想是:以数据表中的每个记录的关键字k为自变量,通过一种函数H(k)计算出函数值。把这个值解释为一块连续存储空间(即数组空间)的单元地址(即下标),将该记录存储到这个单元中。在此称该函数H为哈希函数或散列函数。按这种方法建立的表称为哈希表或散列表。

例如,要将关键字值序列(3,15,22,24),存储到编号为0到4的表长为5的哈希表中。 计算存储地址的哈希函数可取除5的取余数算法H(k)=k% 5。则构造好的哈希表如图所示。

理想情况下,哈希函数在关键字和地址之间建立了一个一一对应关系,从而使得查找只需一次计算即可完成。由于关键字值的某种随机性,使得这种一一对应关系难以发现或构造。因而可能会出现不同的关键字对应一个存储地址。即k1≠k2,但H(k1)=H(k2),这种现象称为冲突把这种具有不同关键字值而具有相同哈希地址的对象称同义词

在大多数情况下,冲突是不能完全避免的。这是因为所有可能的关键字的集合可能比较大,而对应的地址数则可能比较少。

 对于哈希技术,主要研究两个问题:

(1)如何设计哈希函数以使冲突尽可能少地发生。

(2)发生冲突后如何解决。

二、哈希函数

构造好的哈希函数的方法,应能使冲突尽可能地少,因而应具有较好的随机性。这样可使一组关键字的散列地址均匀地分布在整个地址空间。根据关键字的结构和分布的不同,可构造出许多不同的哈希函数。

1.直接定址法

   直接定址法是以关键字k本身或关键字加上某个数值常量c作为哈希地址的方法。该哈希函数H(k)为:

     H(k)=k+c  (c≥0)

  这种哈希函数计算简单,并且不可能有冲突发生。当关键字的分布基本连续时,可使用直接定址法的哈希函数。否则,若关键字分布不连续将造成内存单元的大量浪费。

2.除留余数法

   取关键字k除以哈希表长度m所得余数作为哈希函数地址的方法。即:

     H(k)=k%m   

   这是一种较简单、也是较常见的构造方法。这种方法的关键是选择好哈希表的长度m。使得数据集合中的每一个关键字通过该函数转化后映射到哈希表的任意地址上的概率相等。理论研究表明,在m取值为素数(质数)时,冲突可能性相对较少。  

3.平方取中法

   取关键字平方后的中间几位作为哈希函数地址(若超出范围时,可再取模)。

4.折叠法

   这种方法适合在关键字的位数较多,而地址区间较小的情况。

   将关键字分隔成位数相同的几部分。然后将这几部分的叠加和作为哈希地址(若超出范围,可再取模)。

   例如,假设关键字为某人身份证号码430104681015355,则可以用4位为一组进行叠加。即5355+8101+1046+430=14932,舍去高位。则有H(430104681015355)=4932为该身份证关键字的哈希函数地址。

三、冲突处理方法

假设哈希表的地址范围为0m-l,当对给定的关键字k,由哈希函数H(k)算出的哈希地址为i(0≤i≤m-1)的位置上已存有记录,这种情况就是冲突现象处理冲突就是为该关键字的记录找到另一个“空”的哈希地址。即通过一个新的哈希函数得到一个新的哈希地址。如果仍然发生冲突,则再求下一个,依次类推。直至新的哈希地址不再发生冲突为止。

常用的处理冲突的方法有开放地址法链地址法两大类

1.开放定址法

  用开放定址法处理冲突就是当冲突发生时,形成一个地址序列。沿着这个序列逐个探测,直到找出一个“空”的开放地址。将发生冲突的关键字值存放到该地址中去。

  如 Hi=(H(k)+d(i)) % m, i=1,2,k  (k<m-1)

  其中H(k)为哈希函数,m为哈希表长,d为增量函数,d(i)=dl,d2…dn-l。

  增量序列的取法不同,可得到不同的开放地址处理冲突探测方法。

(1)线性探测法

   线性探测法是从发生冲突的地址(设为d)开始,依次探查d+l,d+2,…m-1(当达到表尾m-1时,又从0开始探查)等地址,直到找到一个空闲位置来存放冲突处的关键字。

   若整个地址都找遍仍无空地址,则产生溢出。

   线性探查法的数学递推描述公式为:

          d0=H(k)

          di=(di-1+1)% m  (1≤i≤m-1)

(2)平方探查法

  设发生冲突的地址为d,则平方探查法的探查序列为:d+12,d+22,…直到找到一个空闲位置为止。

平方探查法的数学描述公式为:

     d0=H(k)

     di=(d0+i*i) % m (1≤i≤m-1)

2.链地址法

用链地址法解决冲突的方法是:把所有关键字为同义词的记录存储在一个线性链表中,这个链表称为同义词链表。并将这些链表的表头指针放在数组中(下标从0到m-1)。这类似于图中的邻接表和树中孩子链表的结构。

3.再哈希法:

方法:构造若干个哈希函数,当发生冲突时,根据另一个哈希函数计算下一个哈希地址,直到冲突不再发  

 

四.  Hash查找过程

 对于给定值 K,计算哈希地址 i = H(K),若 r[i] = NULL  则查找不成功,若 r[i].key = K  则查找成功,否则“求下一地址 Hi”,直至r[Hi] =NULL  (查找不成功)  或r[Hi].key = K  (查找成功) 为止。

查找及性能分析

第一:与装填因子有关

  所谓装填因子是指哈希表中己存入的元素个数n与哈希表的大小m的比值,即=n/m。

 当 越小时,发生冲突的可能性越小,越大(最大为1)时,发生冲突的可能性就越大。

第二:与所构造的哈希函数有关

   若哈希函数选择得当,就可使哈希地址尽可能均匀地分布在哈希地址空间上,从而减少冲突的发生。否则,若哈希函数选择不当,就可能使哈希地址集中于某些区域,从而加大冲突的发生。

第三:与解决冲突的哈希冲突函数有关

  哈希冲突函数选择的好坏也将减少或增加发生冲突的可能性。

原创粉丝点击