好的散列函数的特点:
每个关键字都等可能的散列到m个槽位中的任何一个之中去,并且与其他的关键字已经被散列到哪个槽位中去无关。但是一般都不太可能检查这个条件是否成立,因为很少知道关键字所符合的概率分布。
将关键字解释为自然数:
一般都有方法可以将关键字不是自然数的转化为自然数,如对于字符串,pt,可以被解释为十进制整数对(112,116),这是因为在ASCII中p=112,t=116,然后按照128为基数来表示,故pt可以表示为112*128+116=14,452。这样就把一个字符串转化为了一个整数,虽然这个整数很大。
典型的散列函数:
1、除法散列法
散列函数为:
h(k) = k mod m;
m为散列表的大小。可以选取m的值为与2的整数次幂不太接近的质数。例如散列表中要存放n=2000个字符串,每个字符有8位,一次不成功的查找大约要检查3个元素,因此分配的散列表的大小为m=701.因为此时它接近2000/3、但是有不接近2的任何幂次的质数,然后把每个关键字都是为整数,则我们有散列函数:h(k) = k mod 701;
2、乘法散列法
散列函数为:
h(k) =⌊m(kA-⌊kA⌋)⌋。
m为散列表的大小。对于m、k、A的要求,一般是取m为2的摸个幂次,然后0<A<1,一般取为(√5-1)/2,即0.6180339887.....
3、全域散列法
全域散列的基本思想是再执行开始的时候就从一组仔细设计的函数中,随即的选择一个作为散列函数。这样就可以使散列函数独立于要存储的关键字,保证了没有哪一种输入会是中国导致最坏的情况性态。同时,随机化也使得即使没事对同一个输入,算大也在每次执行时的性态也是不一样的,使得算法具有较好的平均情况性态。
怎样设计一个全域散列函数类?
选择一个足够大的质数p,使得每个可能的额关键字k都落在o到p-1的范围之内(包括0和p-1)。设Zp表示集合{0,1,2,3,.......p-1},Zp*表示{1,2,3,........p-1},然后对于任意的a属于Zp*,b属于Zp,定义散列函数ha,b=((ak+b) mod p) mod m。所有的这样的散列函数就构成了一个散列函数簇Hp,m={ha,b:a属于Zp*,b属于Zp}。由已知,Hp,m中有p(p-1)个散列函数。
上述讲述的是通过链接法来解决碰撞问题,下边讲述通过开放寻址法来解决碰撞。
1、线性探查
2、二次探查
3、双重散列
使用完全散列技术