编程基础篇 · AI 背后的数据结构

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

把 key 亲手塞进桶里,看哈希函数怎么把「翻一遍」变成「直达」;再看两个 key 撞进同一个桶时怎么收场

本页解决的问题

先给结论

「哈希表:为什么它找东西快到不讲理」要解决的关键问题是什么?

把 key 亲手塞进桶里,看哈希函数怎么把「翻一遍」变成「直达」;再看两个 key 撞进同一个桶时怎么收场

判断标准

让这个结论先证明自己值得留下。 把这一页当成决策工具,而不是需要背下来的定义。把概念连到一个真实任务、一个可观察结果,以及一个能改变你判断的失败上。

下一步

写下一个问题:试完这个方法后,你能用什么证据回答它?

常见误区

结论听起来很完整,却没有检查最关键的假设。

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

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

动手玩 · 亲手把 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 在千万在线用户里瞬间找到你的会话——靠的不是翻名单。

「揭底时刻 · 它不翻,它靠算」为什么要看操作

「回到收纳的比喻:大抽屉找东西要挨个翻,是因为你 不知道东西在哪 。哈希表的思路彻底反过来——放进去的那一刻,就用一条固定的公式 算出它该放在几号桶 ;要找的时候,用同一条公式再算一遍,直接开那个桶。这条公式就叫 哈希函数 ,它是一张「定位公式」:不用翻,一步算出在哪」把结构落到了一个具体动作。这里真正要比较的不是名词谁更高级,而是数据如何被放置,以及最常发生的操作需要走多远。

读懂结构,要同时看访问方式和变化方式

「下面是 8 个编号 0 到 7 的桶,和 6 个等着入住的名字。」揭示了一个容易被忽略的取舍:按位置读取、按键查找、从两端进出、插入新元素和遍历关系,适合的组织方式并不相同。一个结构在某个操作上很快,不代表它在所有操作上都快。

  • 哈希函数 = 定位公式 :放和找用同一条公式,位置是算出来的,不是翻出来的
  • 耗时和数据量无关 :6 个人算一次,600 万人还是算一次——这就是版本 B「直达」的真相
  • 碰撞不可怕 :撞桶就在桶里挂条小链;链太长就加桶重排(扩容)

把规模和更新频率一起算进去

实践时可以把「你每次打开 ChatGPT,服务器拿着 session id 在千万在线用户里 瞬间 找到你的会话——靠的不是翻名单」当作边界提醒:先写下数据量、最常用的操作和允许的延迟,再看 AI 给出的结构是否真的匹配。

从「揭底时刻 · 它不翻,它靠算」走到「动手玩 · 亲手把 key 塞进桶」

「揭底时刻 · 它不翻,它靠算」先把问题落在「回到收纳的比喻:大抽屉找东西要挨个翻,是因为你 不知道东西在哪 。哈希表的思路彻底反过来——放进去的那一刻,就用一条固定的公式 算出它该放在几号桶 ;要找的时候,用同一条公式再算一遍,直接开那个桶。这条公式就叫 哈希函数 ,它是一张「定位公式」:不用翻,一步算出在哪」上;到了「动手玩 · 亲手把 key 塞进桶」,讨论继续推进到「下面是 8 个编号 0 到 7 的桶,和 6 个等着入住的名字。 点一个名字 ,看它怎么三步进桶:先把每个字变成电脑里的编码数字并求和,再对 8 求余(因为只有 8 个桶),最后飞进算出来的那个桶。 留意 :全程没有「挨个对比」这个动作,位置完全是算出来的」。两段连起来,重点就不只是记住一个结论,而是看清它成立所依赖的条件。

把这条判断带到下一个场景

遇到一个新的数据结构时,不要从定义开始背。先写出最频繁的操作,再估计数据量和更新方式,最后检查结构是否让这三个条件同时成立。

  • 「揭底时刻 · 它不翻,它靠算」:回到收纳的比喻:大抽屉找东西要挨个翻,是因为你 不知道东西在哪 。哈希表的思路彻底反过来——放进去的那一刻,就用一条固定的公式 算出它该放在几号桶 ;要找的时候,用同一条公式再算一遍,直接开那个桶。这条公式就叫 哈希函数 ,它是一张「定位公式」:不用翻,一步算出在哪
  • 「动手玩 · 亲手把 key 塞进桶」:下面是 8 个编号 0 到 7 的桶,和 6 个等着入住的名字。 点一个名字 ,看它怎么三步进桶:先把每个字变成电脑里的编码数字并求和,再对 8 求余(因为只有 8 个桶),最后飞进算出来的那个桶。 留意 :全程没有「挨个对比」这个动作,位置完全是算出来的
  • 「最后的要点」:验收视角 :看到「在大名单里挨个找」的代码,就该问一句「这里为什么不用哈希?」

最后的「最后的要点」把讨论落到「验收视角 :看到「在大名单里挨个找」的代码,就该问一句「这里为什么不用哈希?」」。回看这条线索时,最值得保留的是:当输入、规模或风险改变,哪些判断需要重新做一遍。

✅ 这一课想和你分享的

  • 哈希函数 = 定位公式:放和找用同一条公式,位置是算出来的,不是翻出来的
  • 耗时和数据量无关:6 个人算一次,600 万人还是算一次——这就是版本 B「直达」的真相
  • 碰撞不可怕:撞桶就在桶里挂条小链;链太长就加桶重排(扩容)
  • 空间换时间:多备一套「定位公式 + 桶」,换来查询自由——AI 世界里 Set、字典、缓存键、去重、session 全是它
  • 验收视角:看到「在大名单里挨个找」的代码,就该问一句「这里为什么不用哈希?」
标记为已学完 阅读进度会自动记录
← 上一篇下一篇 →

继续阅读

同一条线上的下一篇。

文章讨论

读到这里,留下一个判断。

把刚想明白的地方、还没想通的问题,留给下一位一起学习的人。

正在讨论 哈希表:为什么它找东西快到不讲理 AI 背后的数据结构
3条讨论文章讨论 · 与共学社区同步
在共学社区查看
AM
Asha Morgan内容编辑
观点实践记录

我把这篇文章里的一个判断改写成了今天可以验证的小实验。比记住结论更有用的是,知道下一步要观察什么。

文章讨论7 有帮助
LH
Lin Harper独立开发者
观点观点

读完以后我先回头找它成立的条件,而不是直接把方法搬进项目。这个顺序让后面的取舍清楚很多。

文章讨论5 有帮助
KM
Kiki Moore产品运营
问题问题

如果把这个判断放到真实工作里,最先需要补的约束是什么?我想知道从阅读到第一次实践之间,哪一步最值得先做。

文章讨论4 有帮助