算法导论之散列表
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) 种
探查序列。比线性探查和二次探查有很大的提高。
相关文章推荐
- 转_算法导论第11章散列表
- 算法导论第十一章散列表的实现(哈希表的实现)
- 散列表的详细剖析 (算法导论第11章)
- 算法导论之散列表(哈希表)详解(hash table)
- 散列表---算法导论第十一章 Hash Tables
- 散列表的学习和探讨(算法导论第11章)
- Java实现算法导论中反复平方法模取幂
- 【算法导论】最大二分匹配
- 算法导论第24章 单源最短路径
- 【算法导论】学习笔记——第7章 快速排序
- [算法导论] 函数的增长---渐进记号
- 算法导论程序31--红黑树的插入(Python)
- 【算法导论】线性时间选择---从数组中选择第i小的数
- 算法导论15.1 装配线调度问题
- 算法导论(中文版)--chapter 1
- 单连通图(算法导论22.3-12)