编程基础篇 · 哈希与缓存:空间换时间

哈希表:为什么它找东西快到不讲理

还记得第一课的伏笔吗?「查会员名单」的版本 B 用了一行 set.has(user),名单从 100 人涨到 1000 万人,耗时纹丝不动。当时说好第五课揭底——今天就把这个「直达」的魔术拆给你看:它根本不翻,它是算出来的

揭底时刻 · 它不翻,它靠算

回到收纳的比喻:大抽屉找东西要挨个翻,是因为你不知道东西在哪。哈希表的思路彻底反过来——放进去的那一刻,就用一条固定的公式算出它该放在几号桶;要找的时候,用同一条公式再算一遍,直接开那个桶。这条公式就叫哈希函数,它是一张「定位公式」:不用翻,一步算出在哪。

动手玩 · 亲手把 key 塞进桶

下面是 8 个编号 0 到 7 的桶,和 6 个等着入住的名字。点一个名字,看它怎么三步进桶:先把每个字变成电脑里的编码数字并求和,再对 8 求余(因为只有 8 个桶),最后飞进算出来的那个桶。留意:全程没有「挨个对比」这个动作,位置完全是算出来的。

哈希函数(定位公式):把名字变数字 → 对 8 求余 = 桶号
 
 
 
点上面的名字开始 · 每个名字只需入住一次
为什么找的时候也快?因为找和放用的是同一条公式。你问「丽丽在不在?」,哈希表不翻名单,而是当场再算一遍:丽丽 → 40058 → 对 8 求余 = 2 号桶,直接开 2 号桶看一眼。名单有 6 个人是这样,有 600 万人还是这样——算式的耗时和人数无关,这就是「快到不讲理」的全部秘密。
意外情况 · 两个名字撞进同一个桶

你可能已经发现了:阿芳和丽丽算出来都是 2 号桶!这叫哈希碰撞——桶就 8 个,名字千千万,撞桶迟早发生。怎么办?最常用的办法朴素得可爱:在桶里挂一条小链,后来的排在链上(术语叫「链地址法」)。按顺序点下面三个按钮,留意查找时翻了几次。

 
 
撞桶了也只翻了 2 次。直达 2 号桶(不算翻),链上先看到阿芳(第 1 次),再看到丽丽(第 2 次)——比把 6 个名字全翻一遍还是快得多。当然,如果桶太少、链越挂越长,哈希表会自己加桶重排(扩容):比如从 8 个桶变 16 个桶,把所有名字用新公式重新算一遍位置,让每条链重新变短。这就是「空间换时间」——多花点桶,换回直达的速度。
终极对决 · 翻一遍 vs 直达

现在把数据量拉大,让两种找法正面赛跑。选一个数据量,点开跑。留意右边的计数器:不管左边翻到天荒地老,它永远停在 1-2 次。

数据量:
🗄 翻一遍(线性查找)
翻找 0
 
🗃 直达(哈希查找)
翻找 0
 
动画按比例放慢了,真实差距只会更夸张
它在 AI 世界的真身

哈希表可能是你每天被服务次数最多的结构——只是它总躲在幕后。下面四个场景,背后全是同一招「算出位置,一步直达」。

🧰

Set 与字典

第一课版本 B 的 Set、Python 的 dict、JS 的 Map——语言里所有「按 key 取值」的容器,肚子里都是哈希表。

🔑

缓存的键

缓存要在毫秒内回答「这个问题算过吗」,靠的就是把问题哈希成 key 直达查询——下一课的主角。

🧹

去重

训练语料去重、爬虫判断「这个网页抓过没」,都是把内容哈希后进 Set 一查——不然亿级数据两两对比要算到宇宙热寂。

🎫

session 查找

你每次打开 ChatGPT,服务器拿着 session id 在千万在线用户里瞬间找到你的会话——靠的不是翻名单。

✅ 这一课想和你分享的