首页/数据结构/06-search/散列表 🔗 在 Obsidian 中打开
数据结构 · 06-search

散列表

难度 ★★★重要度 ★★★★★ 考查频率 高题型 选择 / 计算 / 综合 散列表哈希表冲突处理
速查
散列表(Hash Table)通过散列函数 $H(key)$ 将关键字映射到存储位置。冲突处理主要用开放定址法(线性/平方/双散列探测)链地址法。ASL 是装填因子 $\alpha$ 的函数。

速查

项目
主题散列表
核心概念根据关键字直接访问的数据结构,通过散列函数将关键字映射到存储位置
难度⭐⭐⭐
重要性⭐⭐⭐⭐⭐

核心概念

一、定义

散列表(Hash Table)是根据关键字直接访问的数据结构。通过散列函数将关键字映射到存储位置:

$$H(key) = 地址$$

二、散列函数

1. 除留余数法

$$H(key) = key \mod p$$

  • p 通常取不大于表长的最大素数
  • 简单常用

2. 直接定址法

$$H(key) = a \times key + b$$

  • 适合关键字连续分布

3. 平方取中法

取关键字平方的中间几位。

4. 折叠法

将关键字分成几部分,相加。

三、冲突处理

1. 开放定址法

线性探测法

$$H_i = (H(key) + i) \mod m, \quad i = 0, 1, 2, ...$$

冲突时,依次探测下一个位置。

问题容易产生堆积(聚集)。

平方探测法

$$H_i = (H(key) \pm i^2) \mod m, \quad i = 0, 1, 2, ...$$

探测位置:$H, H+1, H-1, H+4, H-4, ...$

要求:m 必须是 $4k+3$ 的素数。

双散列法

$$H_i = (H_1(key) + i \times H_2(key)) \mod m$$

用第二个散列函数确定探测步长。

2. 链地址法(拉链法)

将散列到同一位置的关键字用链表连接。

[0] → NULL
[1] → [12] → [25] → people...
[2] → [7] → NULL
[3] → NULL
[4] → [18] → [31] → NULL
优点无堆积问题;删除方便;适合频繁插入删除。

四、装填因子

$$\alpha = \frac{表中记录数}{表长}$$

  • $\alpha$ 越大,冲突越多
  • ASL 是 $\alpha$ 的函数

五、查找效率分析

成功 ASL

$$ASL_{成功} = \frac{\sum 各元素比较次数}{元素个数}$$

失败 ASL

$$ASL_{失败} = \frac{\sum 各位置探测到空的次数}{表长}$$

期望 ASL

  • 线性探测(成功):$\approx \frac{1}{2}(1 + \frac{1}{1-\alpha})$
  • 线性探测(失败):$ASL_{失败} = \frac{1}{2}(1 + \frac{1}{(1-\alpha)^2})$
  • 链地址法(成功):$\approx 1 + \frac{\alpha}{2}$

手算示例

例 1:线性探测法

关键字序列:19, 14, 23, 1, 68, 20, 84, 27, 55, 11, 10, 79

散列函数:$H(key) = key \% 13$,表长 $m = 16$

关键字H(key)探测过程最终位置比较次数
196661
141111
231010101
111→222
683331
207771
8466→7→883
2711→2→3→444
5533→4→553
111111111
101010→11→12123
7911→2→3→4→5→6→7→8→999

散列表(位置 0~15):

位置: 0  1   2  3   4   5   6   7   8   9  10  11  12  13  14  15
数据: -  14   1 68  27  55  19  20  84  79  23  11  10   -   -   -

$ASL_{成功}$ = (1+1+1+2+1+1+3+4+3+1+3+9)/12 = $\frac{30}{12} = 2.5$

例 2:链地址法

关键字序列:19, 14, 23, 1, 68, 20, 84, 27, 55, 11, 10, 79;散列函数 $H(key) = key \% 13$

[0] → NULL
[1] → [14] → [1] → [27] → [79] → NULL
[2] → NULL
[3] → [68] → [55] → NULL
[4] → NULL
[5] → NULL
[6] → [19] → [84] → NULL
[7] → [20] → NULL
[8] → NULL
[9] → NULL
[10] → [23] → [10] → NULL
[11] → [11] → NULL
[12] → NULL

$ASL_{成功} = (10+3+3+1+3+1)/12 = \frac{21}{12} = 1.75$

例 3:失败 ASL 计算

线性探测法,表长 16,散列函数 $H(key) = key \% 13$。失败 ASL 与装填因子 $\alpha$ 有关,需计算每个位置探测到空的次数。

常见考法

考法 1:构造散列表给出关键字序列和散列函数,用线性探测法构造散列表。
考法 2:ASL 计算问:散列表的 ASL 是多少?
考法 3:冲突处理问:线性探测法和链地址法的区别?
考法 4:装填因子问:装填因子对查找效率的影响?答:$\alpha$ 越大,冲突越多,ASL 越大。

易错点

注意
  1. 线性探测是取模:$(H(key)+i) \% m$。
  2. p 取素数:除留余数法中 p 通常取素数。
  3. 失败 ASL 要算到空位置:算到探测为空为止。
  4. 链地址法无堆积;开放定址法有堆积。
  5. 散列表不是有序的:不能折半查找。

核心结论

冲突处理优点缺点
线性探测简单堆积
平方探测减少堆积可能不能探测所有位置
链地址法无堆积,删除方便指针开销

ASL 与 $\alpha$ 的关系:ASL 是 $\alpha$ 的函数,与表长无关。

  • 线性探测:$\approx \frac{1}{2}(1 + \frac{1}{1-\alpha})$
  • 链地址法:$\approx 1 + \alpha/2$

记忆卡片

散列函数最常用什么方法?
除留余数法 $H(key) = key \% p$(p 取素数)。
线性探测法的探测序列?
$H, H+1, H+2, ...$(取模)。
装填因子 $\alpha$ 的定义?
表中记录数 / 表长。
链地址法的优点?
无堆积,删除方便。
散列表能折半查找吗?
不能,散列表无序。
ASL 与什么有关?
ASL 是装填因子 $\alpha$ 的函数,与表长无关。

交互动画 · 线性探测插入

H(key)=key%13,m=16。依次插入 19, 14, 23, 1, 68, 20(线性探测处理冲突) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 蓝框 = 冲突(已占用)的槽,橙框 = 成功放入的槽;插入 key=1 时从 slot1 线性探测到 slot2
点击开始:依次插入 19, 14, 23, 1, 68, 20
点「下一步」或「播放」

相关知识点

sequential-and-binary-search b-tree b-plus-tree block-search

↑ 以上为站内 HTML 相对链接(纯网页可浏览);本页右上「在 Obsidian 中打开」跳回源笔记。