跳到内容

元数据卡

  • 前置知识:数组、链表(第2章)
  • 预计时间:12 分钟
  • 完成标志:能理解哈希函数和哈希表的基本原理

你的进度

你在森林里遇到了一片诡异的树林。每一棵树都长着一个钥匙孔。你没有钥匙,只有一堆五花八门的物品:一支笔、一个苹果、一条蛇、一块石头……

你需要把这些东西塞进树洞里,然后下次拿着相同的东西一碰树洞,它立刻就能把东西吐出来。不需要翻箱倒柜,不需要逐个比对,直接拿到。

这片森林叫做哈希森林


问题:从数组到哈希

假如你要存一个电话本:人名 → 电话号码。用数组怎么存?

python
# 用数组存电话本:需要遍历查找
phone_book = [("Alice", "123"), ("Bob", "456"), ("Charlie", "789")]
# 要找 Bob 的电话:遍历整个数组,O(n)

太慢了。如果能直接把"Bob"变成数组下标(比如 1),那访问就是 O(1)。这就是哈希表的核心思想。

哈希函数把一个任意大小的输入("Bob")映射到一个固定范围内的整数(数组下标)。

python
# 最简单的哈希函数:把字符串转成下标
def simple_hash(key, table_size):
    total = 0
    for char in key:
        total += ord(char)  # 字符的 ASCII 值
    return total % table_size

# "Bob" → (66 + 111 + 98) % 10 = 275 % 10 = 5
# 直接把 Bob 的电话存在数组下标 5 的位置

哈希表的核心操作

python
class HashTable:
    def __init__(self, size=10):
        self.size = size
        self.table = [[] for _ in range(size)]  # 每个槽放一个链表
    
    def _hash(self, key):
        return hash(key) % self.size  # Python 内置 hash 函数
    
    def put(self, key, value):
        index = self._hash(key)
        # 检查 key 是否已存在
        for i, (k, v) in enumerate(self.table[index]):
            if k == key:
                self.table[index][i] = (key, value)
                return
        self.table[index].append((key, value))
    
    def get(self, key):
        index = self._hash(key)
        for k, v in self.table[index]:
            if k == key:
                return v
        raise KeyError(key)

核心操作复杂度:

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

平均情况 O(1) 是因为哈希函数把 key 均匀分布到各个槽。最差 O(n) 是所有 key 都映射到同一个槽——比如哈希函数设计得不好。

哈希函数要满足什么

一个好的哈希函数应该:

  1. 确定性:相同的输入永远产生相同的输出
  2. 均匀分布:不同的输入尽量均匀分布在各个槽
  3. 高效:计算速度快
python
# 不好的哈希函数
def bad_hash(key, size):
    return len(str(key)) % size  # 很多 key 长度相同,全挤一个槽

# 好的哈希函数
def good_hash(key, size):
    h = 0
    for i, char in enumerate(str(key)):
        h = (h * 31 + ord(char)) & 0xFFFFFFFF  # 乘以质数 31
    return h % size

Java 的 String.hashCode() 正是用 h = h * 31 + char 这个公式计算的。


旅人笔记

哈希表用哈希函数把 key 映射到数组下标,实现 O(1) 的平均查找时间。核心三要素:哈希函数(均匀映射)、数组(O(1) 随机访问)、冲突处理(解决多个 key 落在同一槽)。

下一步:哈希冲突解决

好的哈希函数也不能完全避免冲突。当两个 key 映射到同一个槽时,怎么处理?

看冲突怎么解决 →

Built with VitePress | Software Systems Atlas