散列查找
when ? what ? why ? how ?
why
为什么要用散列查找?
what
什么是散列查找?
解决冲突有哪些方法?
how
如何评估查找?
散列函数如何设计?
为什么要用散列查找?
顺序查找 时间复杂度 O(N)
二分查找(前提有序) 时间复杂度 O(logN)
二叉搜索树 时间复杂度 O(h),h 是树高,最好情况 h = logN,最差 h = N 。
二叉平衡树 时间复杂度 O(logN)
如何数据量很大时怎么办,10 亿,100 亿 ?
顺序查找全部遍历不可能 ,二分查找要求有序太难了,2 的 30 次方等于10 亿多,那么二叉搜索树、二叉平衡树最好情况下树高 30,比 30 次也有点多。
散列查找:通过散列函数的计算求出关键字的位置。 这样就有很快的查找效率。时间复杂度几乎是常量 O(1)。
什么是散列查找?
散列 (Hashing) 是一种重要的查找方法。它的基本思想是:以数据对象的关键字 key 为自变量,通过一个确定的函数关系 h,计算出对应的函数值 h(key) ,把这个值解释为数据对象的存储地址(可能不同的关键字会映射到同一个散列地址上会有冲突,需要解决),并按此存放,即“存储位置= h(key)”。
散列表(Hash Table)也称为哈希表。
散列查找的两项基本工作:
- 计算位置:构造散列函数确定关键字存储位置
- 解决冲突:应用某种策略解决多个关键词位置
散列函数如何设计?
散列函数的设计需要考虑下列两个因素:
- 计算简单,以便提高转换速度
- 关键词对应的地址空间分布均匀,以尽量减少冲突
数字关键词的散列函数构造
1.直接定址法
如统计出生年份所对应的人数
h(key) = key -1990
取关键词的某个线性函数值为散列地址
h(key) = a * key + b ( a 、 b 为常数)
直接定址法现实中不常用。
2.除留余数法
h (key) = key mod p
如 h(key) = key % 13
TableSize = n / α, n 是关键词集合大小,α 是允许最大装填因子
一般 p 取素数,p <= TableSize
现实应用中比较常见的方法。
3.数字分析法
如果数字关键词的位数比较多,取比较随机的的位作为散列地址。
4.折叠法
把关键词分割成位数相同的几个部分,然后叠加
5.平方取中法
字符关键词的散列函数构造
1.一个简单的散列函数————ASCII码加和法
对字符关键词 key 定义散列函数如下:
h(key) = (∑key[i]) mod TableSize
函数简单,均匀性比较差,冲突比较严重。
2.简单的改进————前 3 个字符移位法
h(key) = (key[0] * 27^2 + key[1] * 27 + key[2] ) mod TableSize
3.好的散列函数————移位法
涉及关键词所有 n 个字符,并且分布得很好:
解决冲突有哪些方法?
换个位置: 开放地址法
同一位置的冲突对象组织在一起: 链地址法
开放定址法
若发生了第 i 次冲突,试探的下一个地址将增加 di,基本公式是:
1.线性探测
如上图:出现了聚集现象,为了优化出现了下面几种
2.平方探测
平方探测在一定程度上减轻了聚集现象
3.双散列
探测序列应该保证所有的散列存储单元都应该能够被探测到。
p < TableSize, p 、 TableSize 都是素数
4.再散列
当散列元素太多(即装填因子 α 太大),查找效率会下降:实用最大装填因子一般去 0.5 <= α <= 0.85
当装填因子过大时,解决的方法是加倍扩大散列表。
分离链接法
将相应位置上冲突的所有关键词存储在同一个单链表中。
h(key) = key mod 11
散列表的性能分析
平均查重长度(ASL)用来度量散列表查找效率:成功、不成功
影响产生冲突的三个因素:
- 散列函数是否均匀
- 处理冲突的方法
- 散列表的装填因子 α(分离链接法的装填因子是所有地址链表的平均长度)
总结
参考
数据结构 陈越 何钦铭
选择合适的 h(key),散列表的查找效率期望是常数 O(1),它几乎与关键字的空间大小 n 无关!也适合于关键字直接比较计算量大的问题
散列查找是以较小的 α 为前提。因此,散列方法是一个以空间换时间
散列方法的存储对关键字是随机的,不便于顺序查找关键字,也不适合于范围查找,或最大值最小值查找。
开放定址法
- 散列表是一个数组,存储效率高,随机查找。
- 散列表有聚集现象
分离链接法
- 散列表都是顺序存储和链式存储的结合,链表部分的存储效率和查找效率都比较低。
- 太小的 α 可能导致空间浪费,大的 α 又将付出更多的时间代价。不均匀的链表长度导致时间效率的严重下降。
有什么问题欢迎指出,十分感谢!