1. 哈希查找:从“乱放东西”到“快速寻宝”
你有没有过这样的经历?家里的东西随手一放,等要用的时候,翻箱倒柜就是找不到。但如果给每个物品都规定一个固定的位置,比如“钥匙永远挂在门后”,那找起来就快多了。哈希查找的核心思想,其实就是这么回事。
在计算机的世界里,我们经常需要在一大堆数据里快速找到某个特定的值。比如,你要在几百万用户里验证一个登录名和密码是否正确,如果一个个去比对,那得等到猴年马月。这时候,哈希表就派上用场了。它就像一个超级智能的储物柜系统,你告诉它要找的东西叫什么(这个“名字”就是关键字),它通过一个固定的规则(哈希函数)瞬间算出这个东西应该放在哪个柜子里,然后直接去那个柜子拿。理想情况下,一次就能找到,速度快得飞起。
这个“固定的规则”就是哈希函数,比如我们常说的 H(key) = key % K。简单理解,就是拿关键字除以一个数K,取余数,这个余数就是柜子的编号。听起来很简单,对吧?但问题来了,万一两个不同的东西,算出来的柜子编号一样怎么办?就像你和邻居的快递都被分配到了同一个快递柜格子里,这就产生了“冲突”。
处理冲突的方法有很多,今天我们要重点聊的,就是其中一种非常直接的方法——线性探测再散列。你可以把它想象成:当你发现自己的柜子被别人占了,你就顺着柜子一排排往下找,直到找到一个空柜子塞进去。找东西的时候也一样,先去自己的柜子看,没有就继续往下找。这种方法实现起来简单,但它的查找效率,也就是我们常说的平均查找长度,会随着柜子被占得越来越满而发生有趣的变化。今天,我们就来掰开揉碎,看看在这种方法下,查找成功和查找失败,到底各需要“找几次”。
2. 线性探测再散列:当“理想车位”被占后
好了,我们现在知道哈希表是个“智能储物柜”,也知道了哈希函数是分配柜子的规则。那线性探测再散列到底是个什么操作呢?我用一个更生活的例子来讲。
假设你开车去一个大型露天停车场,这个停车场有M个车位(对应哈希表长M)。停车场的管理系统告诉你一个规则:你的车位号等于你的车牌号最后一位数字(假设是0-9,这相当于K=10)。你开着一辆车牌尾号是3的车,高高兴兴地开到3号车位,发现已经被一辆尾号是13的车占了(因为13%10也等于3)。这时候你怎么办?
线性探测的做法就是:别急,从3号车位开始,依次往后查看4号、5号、6号……直到找到一个空车位,然后把车停进去。这个过程就是“探测”。下次你来取车,系统还是会告诉你“你的车在3号位附近”,你先去3号位看,不是你的车,那就继续往后找4号、5号……直到找到你的车为止。这个“依次往后找”的过程,就是线性探测。
在代码里,这个过程通常用一个循环来实现:
int pos = hash(key); // 先计算初始位置
for (int i = 0; i < m; i++) {
pos = (hash(key) + i) % m; // 线性探测下一个位置
if (table[pos] == key) {
// 找到了!
return i + 1; // 查找长度是探测次数+1
} else if (table[pos] == EMPTY) {
// 遇到空位,说明要找的关键字不存在(对于查找而言)
break;
}
}
这里的 i+1 就是查找长度。为什么是i+1?因为第一次查看初始位置算一次,之后每多查看一个位置就再加一次。
这种方法的优点显


2074

被折叠的 条评论
为什么被折叠?



