目录#
[[toc]]
概念#
散列表(Hash Table):
- 是一种根据关键字 (Key) 直接访问存储位置的数据结构。
- 通过哈希函数将关键字映射到表中的一个存储位置(数组下标),从而实现接近 O(1) 的平均插入、删除、查找效率。
| 名称 | 说明 |
|---|---|
| 哈希函数 H(key) | 把关键字映射为表中位置的函数,例如 key % m。 |
| 散列表 | 存放数据的数组或表。 |
| 装填因子 α | 表示存储密度,α = 填入记录数 / 散列表长度。它影响冲突概率和效率。 |
| 方法 | 核心思想 | 说明 |
|---|---|---|
| 开放定址法 | 如果当前位置冲突,就按某种探测序列找下一个空位。 | 例如线性探测、二次探测、再哈希。 |
| 链地址法(拉链法) | 每个表项存一个链表或其他结构,把冲突的关键字都挂在同一个位置。 | 简单灵活,适合装填因子高的情况。 |
- 线性探测再散列:(H(key) + i) % m,i=0,1,2,…
- 二次探测:H(key) + i^2 等。
- 双重散列:使用第二个哈希函数提供步长。
成功查找
找到关键字,比较次数记作查找长度。
失败查找
找不到关键字,但要确认其不存在,平均比较次数称为平均查找失败长度(ASL失败)。
-
对开放定址的线性探测,有公式近似:
其中 α 是装填因子。
| 方法 | 适用场景 |
|---|---|
| 直接定址法:H(key) = key | key 范围小且连续。 |
| 除留余数法:H(key) = key % m | 最常用,m 选为质数可减少冲突。 |
| 数字分析法、折叠法 | key 数字位分布有特点时。 |
| 平方取中法 | key 变化大但中间位较随机时。 |
与其他结构对比#
| 结构 | 查找时间 | 插入删除 | 适用场景 |
|---|---|---|---|
| 顺序表 / 链表 | O(n) | 简单 | 小数据量或频繁遍历 |
| 二叉搜索树 | O(log n) | 保序、范围查询 | 有序数据、高度变化可用平衡树 |
| 散列表 | O(1) (平均) | 高效 | 快速精确查找 |
散列表的核心思想是空间换时间:
通过哈希函数直接定位数据存储位置,使插入、删除、查找在平均情况下都接近 O(1),
例子#

0~10,共 11 个槽位。
| key | key%7 | 备注 |
|---|---|---|
| 87 | 3 | |
| 40 | 5 | |
| 30 | 2 | |
| 6 | 6 | |
| 11 | 4 | |
| 22 | 1 | |
| 98 | 0 | |
| 20 | 6 |
逐步插入并处理冲突(线性探测):
-
87 → 3
-
40 → 5
-
30 → 2
-
6 → 6
-
11 → 4
-
22 → 1
-
98 → 0
-
20 → 6 冲突
6 位置已占 → 7 空,放到 7
0:98
1:22
2:30
3:87
4:11
5:40
6:6
7:20(第一次冲突)
8:空
9:空
10:空
平均查找失败长度#
平均查找失败长度 =
所有空槽位的探测长度总和 ÷ 空槽位数
失败查找从起始地址 H(key) 出发,线性探测直到遇到第一个空槽为止;比较次数 = 扫过的已占槽数 + 1(再比较到那个空槽)。
哈希函数决定了“入口集合”
- 题里给的散列函数是 H(key) = key % 7。
- 这表示任何关键字 key,计算出的哈希地址只可能是 0,1,2,3,4,5,6 七个数。
- 即使散列表物理长度是 11(位置 0~10),也不会有关键字的初始哈希地址是 7、8、9、10。
这点非常关键:
失败查找是从哈希函数给出的起点
既然哈希函数的值域只有 0~6,就不存在从 7~10 作为起点的失败查找。
由于 H(key)=key%7,起点只可能是 0~6。对每个起点计算到最近空槽(8)的长度:
- 从 0:0→1→2→3→4→5→6→7→8 ⇒ 9
- 从 1:1→2→3→4→5→6→7→8 ⇒ 8
- 从 2:2→3→4→5→6→7→8 ⇒ 7
- 从 3:3→4→5→6→7→8 ⇒ 6
- 从 4:4→5→6→7→8 ⇒ 5
- 从 5:5→6→7→8 ⇒ 4
- 从 6:6→7→8 ⇒ 3
求平均(在 7 个可能起点上均匀):
小提醒:如果哈希函数是 % 11(与表长一致),那才应该在 0~10 上平均,此时会得到约 4.27。而本题因为 % 7,只在 0~6 上平均,所以是 6。
- 失败查找:我们找一个根本不存在的 key,哈希函数给出的起始位置一定是 key % 7(也就是 0~6 之间的某个位置)。
- 查找时从这个起点出发,按照线性探测(+1 循环)顺着走,一直走到第一个空槽就停下。
- 这一次探测需要的比较次数,就是该起点到它遇到的第一个空槽之间经过的槽位数 + 1(包括最后那个空槽)。
平均查找成功长度#
对每一个已经插入的元素,都从它的**哈希地址(key % 7)**开始,沿着线性探测的方向查找,直到找到该元素为止。
比较次数 = 探测过的槽位数(包括最后找到它的那个槽位)。
平均查找成功长度 =
所有已插入元素的比较次数总和 ÷ 元素个数。
| 元素 | H(key) | 实际存放位置 | 探测过程 | 比较次数 |
|---|---|---|---|---|
| 87 | 3 | 3 | 3 → 找到 | 1 |
| 40 | 5 | 5 | 5 → 找到 | 1 |
| 30 | 2 | 2 | 2 → 找到 | 1 |
| 6 | 6 | 6 | 6 → 找到 | 1 |
| 11 | 4 | 4 | 4 → 找到 | 1 |
| 22 | 1 | 1 | 1 → 找到 | 1 |
| 98 | 0 | 0 | 0 → 找到 | 1 |
| 20 | 6 | 7 | 6 (被 6 占) → 7 → 找到 | 2 |
- 除了 20 发生冲突需要 2 次比较,其余都只需 1 次。
总比较次数 = 1 + 1 + 1 + 1 + 1 + 1 + 1 + 2 = 9
成熟的哈希函数#
直接用成熟的哈希函数(例如 MD5、SHA 系列、CRC32 等)来做“key → 位置”的映射。
- 数据结构里的哈希函数:
- 目标:快速均匀分布关键字,减少冲突。
- 典型例:key % m、乘法散列法、FNV、MurmurHash。
- 密码学哈希函数(MD5、SHA1/2/3 等):
- 目标:不可逆、抗碰撞、输出分布极随机。
- 应用:数字签名、校验、防篡改、存密码。
只要能把 key 映射成分布均匀的整数,就能作为散列表的哈希函数。
| 场景 | 解释 |
|---|---|
| 分布式缓存 / 数据分片 | 例如一致性哈希(Redis cluster、Cassandra、Ceph),key 往往是字符串。直接用 MD5/SHA1 可以保证跨节点分布更均匀,避免热点。 |
| URL 缩短、文件去重 | 需要避免碰撞和可逆性,MD5/SHA256 直接当 key 更稳。 |
| 跨语言/跨平台 | 不同编程语言都自带 MD5/SHA 实现,开发更方便。 |
| 安全需求 | 不能让外部用户猜到哈希算法的细节时,用强哈希更安全。 |
评论
还没有评论,来发第一个吧
