关于散列(Hash)表的一些总结

来源:互联网 发布:sql not in等价 编辑:程序博客网 时间:2024/05/19 14:20

1、引言

我们讨论的链表和树表的查找中,记录在表中的位置跟记录的关键字之间不存在确定关系,因此,这些表中查找记录时需要进行一系列的关键字比较,查找的效率取决于比较的次数。

散列表是一种根据关键字而直接访问的数据结构,也就是说,散列表建立了关键字和存储地址之间的一种直接映射关系,这里建立映射关系的函数叫散列函数。

散列函数可能会把两个或两个以上的不同关键字映射到同一地址,称这种情况为“冲突”(“碰撞”),这些发生冲突的不同关键字成为同义词。一方面,设计好的散列函数应尽可能减少这种冲突;另一方面,由于有时候这种冲突是不可避免的,所以还要设计好处理冲突的方法。

散列和散列函数的基本特征:1)如果两个散列值是不同的(根据同一函数),那么这两个散列值的原始输入也是不同的,这个特征是散列函数具有确定性的结果;2)但是另一方面,散列函数的输入与输出不是一一对应的,如果两个散列值相同,两个输入值可能是相同的,但并不能绝对肯定二者一定相等;3)散列值的空间往往小于输入的空间,这也是散列的最基本特征,即将任意长度的消息压缩到某一固定长度的消息摘要的函数。


2、散列函数

在构造Hash函数时,必须注意以下几点:

1)Hash函数定义域必须包含全部关键字,而值域则依赖于Hash表的大小或地址范围;

2)Hash函数计算出来的地址应该能等概率、均匀地分布在整个地址空间、以减少冲突的发生;

3)Hash函数应尽量简单,能够在较短时间内就计算出任一关键字对应的存储地址。


3、下面介绍几种常用的Hash函数

1)直接定址法:H(key)=a*key+b  最简单并且不会产生冲突,适合关键字分布基本连续的情况,但实际很少试用这种方法,关键字分布不连续则容易造成存储空间浪费;

2)除留余数法:H(key)=key%p   对p的选择很重要,一般选择最接近于m(Hash表的长度)的质数,可以尽可能地减少冲突,该方法往往是最简单也是最常用的方法;

3)全域散列法:H(key)=(a*key+b)%p  如同除留余数法一样,这里的p应为质数,而a、b的值则在运行时动态确定,多参数共同确定散列结果,可控性更强;

4)随机散列法:如同快速排序中对基准元素的选择一样,随机法往往能避免最坏情况的发生,随机数法通常用于关键字长度不等的情况;

5)平方取中法:取关键字平方后的中间几位作为哈希地址,该方法基于的原理是乘积的中间几位数与乘数的每一位都相关,由此产生的散列地址较为均匀。


4、处理冲突的策略

1)拉链法:拉链发是指把所有的同义词存储在一个线性链表中,这个链表由其散列地址唯一标识;

2)线性探测法:发生冲突后,线性向后试探,找到最近的一个空位置,该方法的缺点在于会出现堆积现象,从而在存取的时候影响效率;

3)平方探测法:与线性探测法不同的是,这里的步长采用1,-1,4,-4,9,-9...这样的策略,它可以避免堆积问题,并且能探测到一半以上的单元;

4)再散列法:该方法需要两个散列函数,当第一个散列函数发生冲突的时则采用第二个散列函数计算出该关键字的地址。


5、散列表的性能分析

散列表的查找过程与构造散列表的过程基本一致,查找过程中,关键码的比较次数取决于产生冲突的多少,产生的冲突少,查找的效率就高,产生的冲突多,查找的效率就低。因此,影响产生冲突多少的因素,也就是影响查找效率的因素。影响产生冲突多少有以下三个因素:

1)、散列函数是否均匀;

2)、处理冲突的方法;

3)、散列表的装填银子。

其中散列表的装填因子定义为:a=填入表中元素的个数/散列表的长度。

实际上,散列表的平均查找长度是装填因子的函数,只是不同的处理冲突的方法有不同的函数,具体公式暂略。