AIGC标识 开放地址法

开放地址法(Open Addressing,也称开放寻址法)是解决哈希表哈希冲突(Collision)的一种核心方法。它的核心思想是:所有元素都直接存储在哈希表数组内部,不使用任何外部数据结构(如链表)。

当插入一个新元素时,如果通过哈希函数计算出的位置已经被占用,程序不会在这个位置拉出链表,而是按照某种规则在数组中往后“探查(Probing)”,直到找到一个空的槽位(Bucket)并将其存入。


一、 开放地址法的插入逻辑

假设哈希表长度为 \(M\),插入键值对 \((key, value)\) 的步骤如下:

  1. 计算初始位置:计算 \(idx = h(key) \bmod M\)。
  2. 检查槽位:
  • 如果 table[idx] 为空,或者带有已删除(TOMBSTONE)标记,直接将元素写入该位置,插入成功。
  • 如果 table[idx] 已有其他元素(发生冲突),则进入探查流程。
  1. 计算下一个位置:根据预设的探查公式计算 \(idx_{next}\),重复步骤 2,直到找到空位置或遍历完整个表(触发扩容)。

二、 常见的 3 种探查规则

假设初始哈希位置为 \(h_0 = h(key) \bmod M\),第 \(i\) 次探查的位置公式 \(h_i\) 如下:

  • 1. 线性探查(Linear Probing)

  • 公式:\(h_i = (h_0 + i) \bmod M\)

  • 逻辑:冲突后依次查看下一个位置(\(h_0+1, h_0+2, h_0+3, \dots\))。

  • 优缺点:缓存命中率高,CPU Cache 非常友好;但容易产生主聚集(Primary Clustering),即连续的被占用槽位会越来越长,严重影响性能。

  • 2. 二次探查 / 平方探查(Quadratic Probing)

  • 公式:\(h_i = (h_0 + c_1 \cdot i + c_2 \cdot i^2) \bmod M\) (如步长依次为 \(+1^2, -1^2, +2^2, -2^2, \dots\))

  • 逻辑:冲突后按步长的平方跳跃探查。

  • 优缺点:有效减缓了主聚集现象;但探查路径固定,可能产生次聚集(Secondary Clustering),且需要谨慎设计数组大小(通常要求为素数)以保证能遍历整个表。

  • 3. 双重哈希(Double Hashing)

  • 公式:\(h_i = (h_0 + i \cdot h_2(key)) \bmod M\)

  • 逻辑:使用第二个独立哈希函数 \(h_2(key)\) 来决定探查的步长。

  • 优缺点:探查步长与键值相关,随机性最好,几乎不会发生聚集现象;但计算开销略大。


三、 开放地址法 vs 链地址法(Chaining)

对比维度 开放地址法 (Open Addressing) 链地址法 (Chaining)
存储结构 仅使用单一连续数组 数组 + 链表/红黑树
装载因子 (\(\alpha = \frac{N}{M}\)) 必须满足 \(\alpha < 1\)(通常超过 0.7 就要扩容) 可以 \(\ge 1\)
内存与 Cache 连续内存,CPU 缓存极其友好,无需额外指针开销 频繁分配节点内存,产生内存碎片,Cache 命中率低
删除操作 较复杂,不能真正置空(需打上墓碑标记,避免断链) 简单,直接从链表中移除节点

在 Python 的 dict、Rust 的 HashMap 以及高性能高性能 C++ 容器(如 Google absl::flat_hash_map)中,通常都优先采用开放地址法(或其变体如 Robin Hood Hashing / Swiss Tables)以追求极致的内存局部性和 CPU Cache 效率。

posted @ 2026-09-14 16:31  西西弗斯身强体健  阅读(37)  评论(0)    收藏  举报