您的位置:首页 > 其它

算法导论之散列表

2011-10-08 21:35 197 查看

散列方法

哈系表有 m 个槽,如何将关键字 key 散列到哈系表的槽中呢?

除法散列方法

散列结果由下列散列函数决定:
hash(key) mod m

其中 m 是 hash 表的大小,注意 m 的选择:

(1) m 不应是 2 的幂。原因分析如下:

散列的发散程度取决于 hash(key) mod m 的分布范围,

假设 m = 2^p, 对 m 取余,结果等于哈系值中二进制的最低 p 位,

如果哈希值最低 p 位的排列很相似,则 hash(key) mod m 的分布

范围很小,关键字的发散程度就不够好。

(2) 经验表明, 取 m 为与 2 的整数幂不太接近的素数。

可以选择与 [ 2^p + 2^(p+1) ] /2 最接近的一个素数。

一个经典的哈系函数

不断地乘以 33, 至于为什么选择 33, 还没足够充分的解释,经验所得。

unsigned long hash(unsigned char *str)
{
unsigned long value = 5381;
unsigned char *p;

for (p = str; *p; p++) {
value = (value << 5) + value + *p;
}

return value;
}


处理冲突的方法

链接法
把散列到同一槽中的所有元素都放在一个链表中。如果 hash 表有 n 个元素,m 个槽。

则一个链中平均存储的元素数,即装载因子 a = n/m ,链接法中装载因子可能大于 1。

最坏情况下,n 个元素都散列到同一个链表,这时查找事件为 O(n)。

平均情况下,查找时间为 O(1+a)

开放寻址法

所有的元素都存放在散列表里,查找一个元素时,计算出该关键字对应的槽,从这个槽开始,

按照一定的顺序探查所有的表项,直到找到所需的元素为止,开放寻址法的装载因子 a 绝对小于 1。

插入一个元素时,先计算该关键字对应的槽,按照一定的顺序探查所有表项,直到找到一个空位为止。

在开放寻址法中,查找和插入都要探查很多槽,对每一个关键字,都存在一个长度为 m 的探查序列,设为:

< h(k, 0), h(k, 1), ... , h(k, m-1) >

理想情况下,探查序列有 m! 种,每个关键字的探查序列是 < 0, 1, ..., m-1 > 的 m! 中排列中

任意一种的可能性是相同的,这称为一致散列。但真正的一致散列很难实现,在实践中,

有三种技术常用来计算开放寻址法中的探查序列: 线性探查,二次探查,和双重探查。

(1) 线性探查

初始探查位置为 t,下一个探查位置为 t+1,再下一个探查 t+2,..., 依次类推,直到 m-1,

然后又卷绕到 0, 1, ..., 直到最后探查槽 t-1。 散列函数为:

h(k, i) = ( h(k) + i ) mod m

在线性探查序列中,一旦确定了初始探查位置,后面的探查位置都固定下来了,

初始探查位置决定了整个序列,而初始探查位置有 m 种,故只有 m 种不同的探查序列。

线性探查方法比较容易实现,但容易出现群集现象,例如: 假设某个时刻,槽

T[k+1] 到 T[k+i] 都被连续占用了,如下图所示:



在这种情况下,下一个插入的关键字,只要其计算出来的初始位置位于区间 [ k+1, k+i+1 ] ,

槽 T[k+i+1] 都会被使用,即槽 T[k+i+1] 被占用的概率为 (i+1)/m。槽 T[k+i+1] 的使用导致

连续占用槽的序列长度增加,引起下一个槽 T[k+i+2] 被使用的概率变得更大。这样恶性循环

下去。连续被占用槽的长度不断增加,查找和插入时间的探查时间也不断增加。这被称为

群集现象。

(2) 二次探查

散列函数为:

h(k, i) = ( h(k) + c1*i + c2* i^2 ) mod m

初始探查位置为 T[ h(k) ], 后续的探查位置在此基础上加上一个偏移,该偏移以二次的方式依赖于 i,

二次探查的效果比一次探查好很多,不过与线性探查一样,初始探查位置决定了整个序列,故只有

m 种不同的探查序列。这会导致一种程度较轻的群集现象,称为二次群集。

(3) 双重散列

双重散列是开放寻址法中最好的探查方法,其散列函数为:

h(k, i) = [ h(k) + i*h'(k) ] mod m

其中 h'(k) 是辅助散列函数。

为了能查找整个散列表,值 h'(k) 要与表的大小 m 互质(结论的证明要利用数论

的知识),确保这个性质的一种方法是令 m 为一个质数,并让 h'(k) 总是产生比

m 小的哈希值。

在双重散列法中,一旦确定了 h(k) 和 h'(k),则关键字 k 的探查序列就确定下来。

所以一个探查序列由 组合< h(k) , h'(k) > 共同决定,对 m 取余数,h(k) 可以产生 m 种

探查序列, h'(k) 也可以产生 m 种探查序列,所以双重散列法可以产生 O(m^2) 种

探查序列。比线性探查和二次探查有很大的提高。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: