散列表

1675 字
8 分钟
散列表
Warning

含AI生成内容

散列表#

概述#

散列表(Hash Table)是一种通过散列函数直接计算存储地址的查找结构。与基于比较的查找算法(如 顺序查找、折半查找)不同,散列查找的理想情况可以达到 O(1) 的时间复杂度。

散列查找的核心思想是:通过 H(key) 将关键字 key 映射为存储地址,从而直接定位目标元素,无需逐一比较。

哈希函数#

除留余数法#

408 考研中最常考的哈希函数是除留余数法

H(key) = key % p

其中 p 取不大于表长的最大素数(或质数),这样可以使关键字的分布更均匀,减少冲突。例如表长 m = 15,则取 p = 13。

装填因子#

装填因子(Load Factor)α = n / m,其中 n 为表中元素个数,m 为表长。

  • α 越大,表越满,冲突概率越高,查找效率越低
  • 拉链法 α 可以大于 1;开放定址法 α 必须小于 1
  • 一般建议 α ≤ 0.75

冲突与同义词#

  • 冲突:key₁ ≠ key₂,但 H(key₁) = H(key₂)
  • 同义词:发生冲突的多个关键字互称同义词

冲突不可避免(鸽巢原理),关键是如何有效处理冲突。

拉链法(链地址法)#

原理#

每个哈希槽位维护一个链表,所有哈希值相同的元素挂在同一链表上:

下标: 0 1 2 ... p-1
槽位: [头指针] [头指针] [头指针] ... [头指针]
↓ ↓ ↓ ↓
链表 链表 链表 链表

查找与插入#

// 拉链法查找
Node* search(HashTable ht, int key) {
int idx = key % MAX_SIZE;
Node *p = ht[idx].head;
while (p != NULL) {
if (p->key == key) return p;
p = p->next;
}
return NULL;
}

插入采用头插法,删除可直接在链表中摘除结点——这是拉链法相比开放定址法的一大优势(开放定址法只能逻辑删除)。

ASL 分析#

指标公式说明
ASL 成功≈ 1 + α/2前提:等概率、头插法
ASL 失败≈ α对所有槽位的空链表和长度求平均

计算示例:哈希函数 H(key) = key % 13,依次插入关键字 {16, 74, 60, 43, 54, 90, 46, 31, 29, 88, 77},表长 m = 13。

关键字H(key)
163
749
608
434
542
9012
467
315
293
8810
7712

H(16) = H(29) = 3,H(90) = H(77) = 12,分别产生冲突挂在同一链表上。

  • ASL 成功 = 各元素比较次数之和 / 11(按实际链表位置计算)
  • ASL 失败 = 各槽位链表长度之和 / 13

易错:拉链法失败 ASL 的分母是表长 m(所有可能的哈希地址数),不是元素个数 n。如果哈希函数模 p 且 p < m,分母应取 p(只有 0~p-1 是合法地址)。

开放定址法#

通用公式#

H_i = (H(key) + d_i) % m, i = 1, 2, 3, ...

其中 d_i 是第 i 次探测的增量,不同的取法对应不同的探测方法。

三种探测方法#

方法增量序列 d_i特点
线性探测法d_i = 1, 2, 3, …依次探测下一个位置,堆积(一次聚集)严重
二次探测法d_i = 1², -1², 2², -2², …探测哈希地址两侧,缓解堆积,表长需为 4k+3 素数
双散列法d_i = i x H₂(key)使用第二个哈希函数,聚集最轻

线性探测法详解#

// 线性探测法查找
int linearSearch(HashTable *ht, int key) {
int pos = key % ht->size;
int i = 0;
while (i < ht->size) {
int cur = (pos + i) % ht->size;
if (ht->data[cur] == EMPTY) return -1; // 遇到空位,失败
if (ht->data[cur] == key) return cur; // 成功
i++;
}
return -1;
}

堆积(Clustering):线性探测法的最大缺陷。同义词和非同义词都可能争夺同一段连续区域,导致大量元素聚集,探测次数急剧增加。

易错:线性探测的堆积是非同义词也会聚集(一次聚集),二次探测的聚集是仅同义词聚集(二次聚集)。

删除问题#

开放定址法不能直接删除元素。如果直接将位置置空,会导致后续探测到此位置时误认为查找失败,中断探测链。

解决方案:使用墓碑标记(Tombstone / 懒删除)——将被删除位置标记为 DELETED 状态,查找时跳过,插入时可复用。

ASL 计算示例#

m = 11,H(key) = key % 11,依次插入 19, 1, 23, 14, 55:

下标: 0 1 2 3 4 5 6 7 8 9 10
元素: 55 1 23 14 - - - - 19 - -

查找成功 ASL = (1 + 1 + 2 + 1 + 1) / 5 = 6/5 = 1.2

查找失败 ASL:对每个哈希地址(0~10),探测到空位所需的比较次数求平均。

地址012345678910
比较次数54321111211

ASL 失败 = (5+4+3+2+1+1+1+1+2+1+1) / 11 = 22/11 = 2.0

易错:查找失败 ASL 的分母是表长 m(所有可能的哈希地址数),不是元素个数 n。如果 H(key) = key % p 且 m != p,分母应取 p。408 真题中 m 和 p 经常不同,这是最常见的丢分点。

拉链法与开放定址法对比#

比较维度拉链法开放定址法
冲突处理链表存储同义词探测下一个空位
装填因子 α可大于 1必须小于 1
删除操作直接删除结点只能逻辑删除(打标记)
堆积现象不会产生会产生聚集
空间利用需额外指针空间无额外指针开销
ASL 成功≈ 1 + α/2线性探测:½(1 + 1/(1-α))
ASL 失败≈ α线性探测:½(1 + 1/(1-α)²)
适用场景表长不确定、频繁增删表长已知、较少删除

散列查找复杂度#

操作平均时间复杂度最坏时间复杂度说明
查找O(1+α)O(n)平均取决于装填因子 α
插入O(1+α)O(n)需先查找位置
删除O(1+α)O(n)拉链法直接删,开放定址法墓碑标记

空间复杂度:拉链法 O(m+n),开放定址法 O(m)。

考研高频考点#

  • 给定关键字序列和哈希函数,画出哈希表并计算 ASL(选择/填空/大题必考)
  • ASL 成功和 ASL 失败的计算(几乎每年必考,注意分母区分)
  • 拉链法 vs 开放定址法的优缺点对比(简答题高频)
  • 线性探测法的堆积问题:同义词和非同义词共同聚集(概念辨析)
  • 开放定址法不能直接删除的原因及墓碑标记方案(简答题高频)
  • 装填因子 α 的定义及对查找效率的影响(概念题)
  • 除留余数法中 p 的选取原则(取不大于表长的最大素数)
  • 冲突与同义词的概念辨析(选择题)

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

散列表
https://lingluoa.icu/posts/数据结构/hash-table/
作者
lingluoa
发布于
2026-07-07
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
lingluoa
Hello, I'm lingluoa.
公告
欢迎来到我的博客!不定期更新中。
文章目录
标签
站点统计
文章
53
分类
9
标签
75
总字数
175,771
运行时长
0
最后活动
0 天前
站点信息
构建平台
ESA Pages
博客版本
Firefly v6.15.6
文章许可
CC BY-NC-SA 4.0