算法导论 11章 散列表

来源:互联网 发布:java微信商城源码 编辑:程序博客网 时间:2024/05/22 11:33

散列表是实现字典操作的一种有效数据结构

散列表是普通数组概念的推广

散列表使用一个长度与实际存储的关键字数目成比例的数组来存储,根据关键字计算相应的下标。

直接寻址表

缺点:

(1)如果U很大,要保存|U|大小的一张表T有点不实际。(2)实际存储的关键字集合K相对U来说可能很小,因而分配给T的大部分空间都要浪费掉。

散列表

利用散列函数K计算出槽的位置。

冲突:两个关键字可能映射到同一个槽中。

冲突解决方法:
1)链接法

装载因子:给定一个能存放n个元素的、具有m个槽位的散列表T,装载因子定义为n/m。含义是一个链中平均存放的元素数。简单一致散列:任何元素散列到m个槽中每一个的可能性是相同的,且与其他元素已被散列到什么位置独立无关。定理:对一个用连接技术来解决碰撞的散列表,在简单一致散列的假设下,一次不成功查找的期望时间为θ(1+α),这个时间包含计算h(k)的时间。定理:在简单一致散列的假设下,对于用链接技术解决碰撞的散列表,平均情况下一次成功的查找需要θ(1+α)时间。

2)开放寻址法

散列函数

好的散列函数的特点:

每个关键字都被等可能的散列到M个槽位中的任何一个,与其他关键字已散列到哪个槽位无关将这些相近符号散列到相同槽中的可能性最小化。

除法散列法

通过取k除以m的余数,将关键字k映射到m个槽的某一个中去。散列函数为:h(k)=k mod m 。m不应是2的幂,通常m的值是与2的整数幂不太接近的质数。

乘法散列法

乘法散列法构造散列函数需要两个步骤。第一步,用关键字k乘上常数A(0<A<1),并抽取kA的小数部分。然后,用m乘以这个值,再取结果的底。散列函数如下:h(k) = m(kA mod 1)。

全域散列法

 给定一组散列函数H,每次进行散列时候从H中随机的选择一个散列函数h,使得h独立于要存储的关键字。全域散列函数类的平均性能是比较好的。

开放寻址法

线性探查

h(k, i) = (h'(k) + i) mod m, i = 0, 1, ... ,m - 1在线性探查方法中,初始探查位置确定了整个序列,因此只有m种不同的探查序列。且它存在一次群集现象。

二次探查

h(k, i) = (h'(k) + c * i + d * i^2) mod m其中c和d为辅助常数。初始探查位置为T[h'(k)],后续的探查位置要在此基础上加上一个偏移量,该偏移量以二次的方式依赖于探查号i。它比线性探查好的多。但是如果两个关键字初始探查位置相同,那么他们的探查序列也是相同的。因此它存在程度较一次群集轻的二次群集现象。

双重散列

h(k, i) = (f(k) + i * g(k)) mod m其中f(k)和g(k)为辅助散列函数。与线性探查和二次探查不同的是,这里的探查序列以两种方式依赖于关键字k。为能查找整个散列表,值g(k)要与m互素。可以取m为2的幂,并设计g(k)为总产生奇数。或者取m为素数,设计g(k)总产生比m小的正整数。比如:可以取m为素数,并取f(k) = k mod m, g(k) = 1 + (k mod n) 其中n略小于m(比如为m - 1)。双重散列法用了θ(m^2)种探查序列。

完全散列

如果某一种散列技术在进行查找时,其最坏情况内存访问次数为O(1)的话,则称其为完全散列(perfect hashing)。通常利用一种两级的散列方案,每一级上都采用全域散列。为了确保在第二级上不出现碰撞,需要让第二级散列表Sj的大小mj为散列到槽j中的关键字数nj的平方。如果利用从某一全域散列函数类中随机选出的散列函数h,来将n个关键字存储到一个大小为m=n的散列表中,并将每个二次散列表的大小置为mj=nj2 (j=0, 1, …, m-1),则在一个完全散列方案中,存储所有二次散列表所需的存储总量的期望值小于2n。

0 0