Skip to content

哈希表原理:从 Word 拼写检查说起

场景引入

你在 Word 里打字,每个单词下面如果拼错了会立刻出现红色波浪线。Word 的字典里有几十万个单词,它是怎么做到瞬间判断一个单词是否存在的?

答案就是哈希表。它能在 O(1) 时间内完成查找、插入和删除操作。

核心原理

哈希函数

哈希函数的作用是把任意大小的 key 映射到固定范围的数组下标

javascript
function hash(key, tableSize) {
  let h = 0;
  for (let i = 0; i < key.length; i++) {
    h = (h * 31 + key.charCodeAt(i)) % tableSize;
  }
  return h;
}

好的哈希函数应满足:

  1. 确定性:相同的 key 一定得到相同的值
  2. 均匀性:不同的 key 尽量分散到不同的槽位
  3. 高效性:计算速度快

冲突解决

不同的 key 可能映射到同一个位置,这就是哈希冲突。两种主要解决策略:

拉链法(Chaining)

每个槽位存一个链表,冲突的元素追加到链表尾部。

javascript
class HashMapChaining {
  constructor(size = 16) {
    this.table = new Array(size).fill(null).map(() => []);
    this.size = size;
  }

  _hash(key) {
    let h = 0;
    for (const ch of String(key)) {
      h = (h * 31 + ch.charCodeAt(0)) % this.size;
    }
    return h;
  }

  put(key, value) {
    const idx = this._hash(key);
    const bucket = this.table[idx];
    for (const pair of bucket) {
      if (pair[0] === key) { pair[1] = value; return; }
    }
    bucket.push([key, value]);
  }

  get(key) {
    const idx = this._hash(key);
    for (const pair of this.table[idx]) {
      if (pair[0] === key) return pair[1];
    }
    return undefined;
  }
}

优点:实现简单,删除方便 缺点:链表过长时退化为 O(n)(Java 8 中 HashMap 链表超过 8 个会转红黑树)

开放寻址法(Open Addressing)

冲突时探测下一个空位。常见策略:

  • 线性探测(hash + i) % size
  • 二次探测(hash + i²) % size
  • 双重哈希(hash1 + i * hash2) % size
javascript
// 线性探测示例
put(key, value) {
  let idx = this._hash(key);
  while (this.table[idx] !== null && this.table[idx].key !== key) {
    idx = (idx + 1) % this.size; // 线性探测
  }
  this.table[idx] = { key, value };
}

优点:缓存友好(数据连续存储) 缺点:删除复杂(需要标记墓碑),容易聚集

负载因子与扩容

负载因子 = 已存元素数 / 数组容量

  • 拉链法一般在负载因子 > 0.75 时扩容(Java HashMap 的默认阈值)
  • 开放寻址法一般在 > 0.5 时就需要扩容

扩容 = 创建 2 倍大小的新数组 → 所有元素重新哈希插入(rehash)。

工业级哈希表的设计要点

要点说明
初始容量通常为 2 的幂(方便位运算取模)
负载因子阈值0.75 是性能和空间的平衡点
扩容策略2 倍扩容,渐进式 rehash(Redis 做法)
哈希函数乘法+位运算混合,避免简单取模
冲突退化链表 → 红黑树(Java 8 HashMap)

复杂度分析

操作平均最坏
查找O(1)O(n)
插入O(1)O(n)
删除O(1)O(n)

最坏情况:所有 key 哈希到同一个槽位,退化为链表。

面试要点

  • 理解哈希表为什么是 O(1)(哈希函数 + 数组随机访问)
  • 知道冲突解决的两种策略及各自优劣
  • 了解扩容机制和负载因子
  • 能手写一个简单的哈希表

面试算法可视化图解