学校图书馆要录入 10 万本图书的编号。管理员小林遇到了两只拦路虎。
第一个问题:找书太慢。如果所有书都堆在书架上,要找「编号 20250315」这本书,只能一本一本翻,平均要翻 5 万次。小林想:能不能一进门就知道它在第几层?
第二个问题:编号不连续。有的书编号是 1,有的书编号是 987654321。如果按编号直接排位置,中间会出现巨大的空档,书架根本不够摆。
老图书馆员递给他一张纸,上面写着一个办法:「把编号除以 9973 取余数,余数就是书架的层号。」
小林一试:编号 20250315 ÷ 9973 余 3471,那就放到第 3471 层。下次找书时再算一次余数,直接奔第 3471 层,一步到位!
但很快他又发现问题:编号 3471 和编号 9973 + 3471 = 13444 算出来的余数都是 3471,它们要抢同一个位置。这就是「哈希冲突」。
哈希 = 用一个函数把「巨大的键空间」压进「有限的地址空间」;冲突不可避免,只能被管理。
💡 故事背后的算法思想
哈希的本质是「空间换时间」:多开一个表(空间),换来 O(1) 的查找(时间)。这是竞赛里最划算的交易之一。
哈希函数的关键:它必须是「确定性」的 —— 同一个键永远算出同一个地址;同时要尽可能「均匀」,让不同的键尽量分散到不同的桶里。
冲突为什么不可避免:键空间是无穷(或极大)的,而地址空间是有限的。由抽屉原理,一定存在两个不同的键映射到同一个地址。所以真正的工程问题不是「消除冲突」,而是「高效处理冲突」。
本讲的重点:数值哈希函数怎么设计(直接定址、除留余数、乘法散列、平方取中),以及如何用桶 / 链地址法把冲突管理好。字符串版本的哈希留到下一讲 s4-04。