分类目录:《算法设计与分析》总目录
相关文章:
·散列表/哈希表(Hash Table)(一):基础知识
·散列表/哈希表(Hash Table)(二):直接寻址表
·散列表/哈希表(Hash Table)(三):散列表原理
·散列表/哈希表(Hash Table)(四):散列函数
·散列表/哈希表(Hash Table)(五):开放寻址法
·散列表/哈希表(Hash Table)(六):完全散列
使用散列技术通常是个好的选择,不仅是因为它有优异的平均情况性能,而且当关键字集合是静态时,散列技术也能提供出色的最坏情况性能。所谓静态,就是指一旦各关键字存入表中,关键字集合就不再变化了。一些应用存在着天然的静态关键字集合,如程序设计语言中的保留字集合,或者CD-ROM上的文件名集合。一种散列方法称为完全散列,如果该方法进行查找时,能在最坏情况下用 O ( 1 ) O(1) O(1)次访存完成。
我们采用两级的散列方法来设计完全散列方案,在每级上都使用全域散列。下图描述了该方法:
第一级与带链接的散列表基本上是一样的:利用从某一全域散列函数簇中仔细选出的一个散列函数
h
h
h,将
n
n
n个关键字散列到
m
m
m个槽中。
然而,我们采用了一个较小的二次散列表 S j S_j Sj及相关的散列函数 h j h_j hj,而不是将散列到槽 j j j中的所有关键字建立一个链表。利用精心选择的散列函数 h j h_j hj,可以确保在第二级上不出现冲突。
但是,为了确保在第二级上不出现冲突,需要让散列表 S j S_j Sj的大小 m j m_j mj为散列到槽 j j j中的关键字数 n j n_j nj的平方。尽管 m m m对 n n n的这种二次依赖看上去可能使得总体存储需求很大,但我们会在后面说明,通过适当地选择第一级散列函数,可以将预期使用的总体存储空间限制为 O ( n ) O(n) O(n)。
我们采用的散列函数是《算法设计与分析——散列表[哈希表](四):散列函数》中的全域散列函数类。第一级散列函数选自类 m m m,其中 p p p是一个比任何关键字值都要大的素数。那些散列到槽 j j j中的关键字通过利用一个从类 H p , m j \mathcal{H}_{p,m_j} Hp,mj中选出的散列函数 h j h_j hj,被重新散列到一个大小为 m j m_j mj的二次散列表 S j S_j Sj中。
下面分两步进行。首先,要确定如何才能保证第二级散列表中不发生冲突。其次,要说明使用总体存储空间的期望数为 O ( n ) O(n) O(n),这里包括主散列表和所有的二级散列表所占的空间。
如果从一个全域散列函数类中随机选出散列函数 h h h,将 n n n个关键字存储在一个大小为 m = n 2 m=n^2 m=n2的散列表中,那么表中出现冲突的概率小于 1 2 \frac{1}{2} 21。
上述引用所描述的情形(即 m = n 2 m=n^2 m=n2)中,对于一个从 H \mathcal{H} H中随机选出的散列函数 h h h,较有可能不发生冲突。给定待散列的包含 n n n个关键字的集合 K K K(注意 K K K是静态的),只需几次随机的尝试,就能比较容易地找出一个没有冲突的散列函数 h h h。
但当 n n n比较大时,一个大小为 m = n 2 m=n^2 m=n2的散列表还是很大的。因此,我们采用两级散列方法,并利用上述的做法,对每个槽中的关键字仅进行一次散列。一个外层的(或称为第一级的)散列函数 h h h用于将各关键字散列到 m = n m=n m=n个槽中。那么,如果有 n j n_j nj个关键字被散列到了槽 j j j中,可以用一个大小为 m j = n j 2 m_j=n_j^2 mj=nj2的二级散列表 S S S来提供无冲突的常数时间查找。
现在再来看看如何确保所用总体存储空间为 O ( n ) O(n) O(n)的问题。由于第 j j j个二级散列表的大小 m m m以所存储的关键字数 n n n的平方方式增长,因而存在着这样一种风险,即所需的总体存储空间量可能会很大。
如果第一级散列表的大小为 m = n m=n m=n,则用于存储主散列表、大小为 m j m_j mj的二级散列表,以及用于存储二次散列函数 h j h_j hj的参数 a j a_j aj和 b j b_j bj的存储空间总量为 O ( n ) O(n) O(n)。