Appearance
第 5 讲 哈希表
本讲知识图谱
图表渲染中…
5.1 从动态集合到符号表
哈希表主要用于实现字典、集合、符号表等动态集合。典型操作是:
INSERT(k, v):插入键值对。SEARCH(k):按键查询。DELETE(k):按键删除。
如果只需要等值查询,哈希表通常比平衡搜索树更快,期望时间接近
5.2 直接寻址
若键来自较小宇宙集合
text
DIRECT-ADDRESS-SEARCH(T, k):
return T[k]
DIRECT-ADDRESS-INSERT(T, x):
T[x.key] = x
DIRECT-ADDRESS-DELETE(T, x):
T[x.key] = nil直接寻址时间
哈希表用哈希函数把大键空间压缩到较小表长:
问题随之出现:不同键可能映射到同一槽位,即冲突。
5.3 冲突与链地址法
冲突不可避免,因为通常
text
CHAINED-HASH-INSERT(T, x):
insert x at head of list T[h(x.key)]
CHAINED-HASH-SEARCH(T, k):
search key k in list T[h(k)]
CHAINED-HASH-DELETE(T, x):
delete x from list T[h(x.key)]若链表是双链表且删除时已持有节点指针,删除可为
装载因子:
其中
5.4 链地址法分析
简单均匀散列假设:每个键等概率映射到
在该假设下,不成功搜索的期望时间为:
因为先计算哈希值
成功搜索也为
若保持
5.5 哈希函数选择
好的哈希函数应尽量把真实数据分布均匀打散,避免规律键集中到少数槽位。
除法散列:
表长
乘法散列:
其中
字符串哈希常把字符串看成多项式:
实际中需要注意溢出、字符编码、攻击性输入和随机种子。
5.6 全域哈希
固定哈希函数总可能被构造出坏输入,使所有键冲突。全域哈希通过随机选择哈希函数来避免对抗性坏例。
设
则
结论:若从全域哈希族中随机选
一种经典构造:
- 取素数
。 - 随机选择
, 。 - 定义:
函数族
5.7 最长连续子序列
书面作业 2 Q1 要求在线性期望时间内求数组中最长连续整数子序列长度。关键是用哈希集合支持
算法:
- 把所有元素放入集合
。 - 对每个数
,只有当 时,才把 当作一个连续段的起点。 - 从
开始不断查 是否存在,更新最长长度。
python
def longest_consecutive(nums):
s = set(nums)
best = 0
for x in s:
if x - 1 not in s:
y = x
while y + 1 in s:
y += 1
best = max(best, y - x + 1)
return best为什么是期望
5.8 哈希表与搜索树对比
| 需求 | 哈希表 | 平衡搜索树 |
|---|---|---|
| 等值查询 | 期望 | |
| 插入删除 | 期望 | |
| 有序遍历 | 不自然 | 支持 |
| 最小/最大 | 需额外维护 | 支持 |
| 前驱/后继 | 不支持或很难 | 支持 |
| 最坏保证 | 需随机化或特殊设计 | 确定 |
| 空间 | 依赖装载因子 | 每节点指针和颜色等 |
作业定位
- 书面作业 2 Q1:最长连续子序列,使用哈希集合识别每个连续段的起点。
- 该题的“expected linear running time”来自哈希表的期望
查询,不是比较排序。若先排序,复杂度为 ,不能拿满分。
本讲易错点
- 哈希表不是不会冲突;它通过冲突处理和好的哈希函数控制冲突代价。
是期望或摊还意义下的常见结论,坏哈希函数下可能退化。 - 装载因子
可以大于 1,尤其在链地址法中。 - 直接寻址的键必须能直接作为数组下标,且宇宙集合不能太大。
- 除法散列中表长选择会影响分布。
- 全域哈希随机的是哈希函数,而不是每次查询结果。
自测题
- 直接寻址和哈希表的根本区别是什么?
- 写出链地址法的插入、查询、删除操作。
- 解释装载因子
对期望查询时间的影响。 - 为什么固定哈希函数可能被对抗性输入击穿?
- 写出全域哈希的定义。
- 证明最长连续子序列算法中每个元素最多被连续扫描一次。