散列表最重要的是散列函数的选择,一个好的散列函数应满足简单均匀散列假设特点:每个关键字都被等可能的散列到m个槽位中的任何一个,并与其它关键字已散列到哪个槽位无关。
遗憾的是上述条件很难检测到是否满足,因为很少能知道关键字散列所满足的概率分布,且各关键字可能并不是完全独立的。
实际中,可应用启发式方法构造性能好的散列函数。设计过程中,充分利用关键字分布的有用信息。
“除法散列”是一种较好的方法,此时应避免选择m的某些值,如m不应为2的幂。一个不太接近2的整数幂的素数,常常是m的一个较好的选择。
散列表的冲突解决方法:链接法和开放寻址法。
在一个必须使用DELETE关键字的散列表应用中,更常见的做法是采用链接法来解决冲突。因为,如果采用开放寻址法,那么因为搜索时有DELETE标识干扰,会绕过DELETE,查找事件就不再依赖于装载因子α了。
散列表一个重要的概念就是全域散列函数H。
那么,什么是全域散列函数呢?如何构造全域函数H呢?
全域散列函数是为了解决普通散列函数存在的一个不可避免的缺点而设计的,针对普通散列函数:
拿网易公开课上(http://open.163.com/movie/2010/12/3/A/M6UTT5U0I_M6V2TGI3A.html)charles举的那个例子:如果你和一个竞争对手同时为一家公司做compiler的symbol table, 公司要求你们代码共享,你们做好后公司评判的标准就是你俩互相提供一些测试样例,谁的效率高就买谁的。
然后,普通哈希的缺点就出来了:对任意的hash函数h,总存在一组keys,使得对某个槽i,总可以找到一组键值,让他们都映射到同一个槽里面,这样效率就跟链表差不多了。
解决的思想就是:独立于键值,随机的选择hash 函数。这就跟快排中为避免最差情况时随机化版本差不多。但是选取hash function的全局域是不能乱定的,否则也达不到理想的性能。
知道了全域散列函数的目的,那么如何构建全域散列函数呢?
下面给出全域哈希的定义:
设U是key的全局域, 设
H
是哈希函数的有限集合,每一个都是将U映射到{0,1,..,m-1},即table的槽内。 如果对所有不等的x,y∈U,有。
换句话说,就是对于任意的不相等key的x和y, 从哈希函数集中随机选择一个哈希函数,这两个key发生冲突的概率是1/m。
更形象的,当我随机选一个哈希函数时,就像在上图区域乱扔一个飞镖,落在下面红色区域中就会发生冲突,这个概率是1/m。
下面给出一个定理来说明为什么全域哈希函数H是好的。
设h是从哈希函数全域集 H 中随机选出的函数h. h被用作把任意n个键映射到表T的m个 槽中,对给定键值x,我们有:定理:E[#collision with x]<n/m,即两个x相互碰撞的概率的期望小于n/m.
定理证明:
Proof: 设 Cx 是表示与key x冲突的键值数量的随机变量,设 cxy 是指示变量,即
则, E[cxy]=1/m 且 Cx=∑y∈T−{x}cxy ,则
这个定理想要说明的是,这种全域哈希的随机化选择可以达到哈希表理想的效果。注意这里n/m是之前定义过的load factor,装载因子α。
下面给出一种构造全域哈希函数的方法:
首先选择一个足够大的质数p,使得所有的键值都在0-p-1之间。且设 Zp 表示{0,1,...,p-1},设
Z∗p 表示{1,2,..,p-1}. 因为槽m的数量少于key的数量,所有m<p。然后我们就可以设计哈希函数了,设任意的
a∈Z∗P,b∈Zp ,然后
ha,b(k)=((ak+b)modp)modm
所有这样的哈希函数族为:
Hp.m={ha,b:a∈Z∗p,b∈Zp}
例如:选定p=17,m=6, h3,4(8)=5 . 每个哈希函数都是将 Zp 映射到 Zm 。我们还可以看到这个哈希函数族共有p(p-1)个哈希函数。
除了全域哈希,还有一个更好的但是更复杂的哈希散列结构,就是完美哈希。完美哈希是采用两层散列框架,每一层都是全域哈希散列,最坏查找性能O(1)。下面给一个参考链接:https://www.cnblogs.com/soyscut/p/3396216.html