hash地址_Hash冲突之开放地址法

63705455e734534ed6dbc09f0a5c57c3.png

来源: 趣谈编程 作者: 趣谈编程

Hash碰撞

e061274030e1f65c11da76a60a8dbb21.png

慧能

0a6fe1a61b359b0f59e84fc91023f825.png

一尘,国庆节过完了,还记得Hash函数吗?

当然记得了,Hash函数就是将任意长度的输入转化成固定长度的输出的一类函数

d8728d9e31fa2336b950ebaaf737a460.png 7978784058df1e02aa037741fc5ae63b.png

一尘

比如说我的输入是任意一个自然数(0,1,2,3...),而我要求经过一个函数后我的输出的数的范围要在0-9这样一个范围之间。

35f20988720731724c62b01450b79123.png

很容易想到,我们可以使用Hash函数:

57675655a77ad63384560c59979340e9.png

其中key就是输入

在哈希表(散列表)里,Hash函数的作用就是将关键字Key转化为一个固定长度数组的下标,以便存取键值对

639fbb88800f1a318255e643cc6ab676.png e061274030e1f65c11da76a60a8dbb21.png

慧能

0a6fe1a61b359b0f59e84fc91023f825.png

不错,还记得,那当多个键(key)经过Hash函数处理后落在了同一个位置时怎么办呢?

18feb0b9c3d576a3f24b50daf46d88b5.png

我记得上次师傅给我讲过链地址法

d8728d9e31fa2336b950ebaaf737a460.png 7978784058df1e02aa037741fc5ae63b.png

一尘

链地址法可看:神速Hash

开放地址法

e061274030e1f65c11da76a60a8dbb21.png

慧能

0a6fe1a61b359b0f59e84fc91023f825.png

记性不错,但还有一种方法叫开放地址法

哦?开放地址

d8728d9e31fa2336b950ebaaf737a460.png 7978784058df1e02aa037741fc5ae63b.png

一尘

所谓开放地址法就是发生冲突时在散列表(也就是数组里)里去寻找合适的位置存取对应的元素。

834d783b9ed1f6ae45955f37bdcd5173.png

这个合适的位置该怎么找呢?

d8728d9e31fa2336b950ebaaf737a460.png 7978784058df1e02aa037741fc5ae63b.png

一尘

e061274030e1f65c11da76a60a8dbb21.png

慧能

0a6fe1a61b359b0f59e84fc91023f825.png

问的好,为师给你说说几种方法

0b3a6691a70ce23b524abcfcd63819d5.png

线性探测法

2c0c52f2fd183a17b6ee6b21b9542ebc.png

最容易想到的就是当前位置冲突了,那我就去找相邻的下一个位置。

就拿放入元素举例吧,当你放入到下标为2的位置后,另一个键值对也落入了这个位置,那么它就向后依次加一寻找合适的位置,然后把放入进去。

02d3a3abd578ad270ad601e56826a473.png fec2016c732045da0423d4c69df0ab3a.png

我们把这种方法称作线性探测法,我们可以将Hash以及寻找位置的过程抽象成一个函数:

f0bb7e8040b5bd76edfcb1a99bce516a.png

所以关键字要进行查找或者插入,首先看(hash1(key)+0)%7 位置是自己最终的位置吗?如果有冲突,就探测(查看)下一个位置:(hash1(key)+1)%7。依次进行

所谓探测,就是在插入的时候检查哪个位置可以插入,或者查找时查找哪个位置是要查找的键值对,本质就是探寻这个键值对最终的位置。

但是这样会有一个问题,就是随着键值对的增多,会在哈希表里形成连续的键值对

ef794959f1f5a7ac52b27018e9548014.png

这样的话,当插入元素时,任意一个落入这个区间的元素都要一直探测到区间末尾,并且最终将自己加入到这个区间内。这样就会导致落在区间内的关键字Key要进行多次探测才能找到合适的位置,并且还会继续增大这个连续区间,使探测时间变得更长,这样的现象被称为“一次聚集(primary clustering)”

b48e765ea226499b316f39984ca3cb17.png bc912ac42a653b630e7e9827c1da1ab1.png

这可如何是好

d8728d9e31fa2336b950ebaaf737a460.png 7978784058df1e02aa037741fc5ae63b.png

一尘

0b3a6691a70ce23b524abcfcd63819d5.png

平方探测法

2c0c52f2fd183a17b6ee6b21b9542ebc.png e061274030e1f65c11da76a60a8dbb21.png

慧能

0a6fe1a61b359b0f59e84fc91023f825.png

我们可以在探测时不一个挨着一个地向后探测,我们可以跳跃着探测,这样就避免了一次聚集。

其实我们可以让它按照 i^2 的规律来跳跃探测

aa8c747c79bf77ebbb79f4e04f22803c.png fe3e4aa8b5c9dec51f6b0ace0150d412.png

这样的话,元素就不会聚集在某一块区域了,我们把这种方法称为平方探测法

同样我们可以抽象成下面的函数:

e359e3d1ddf4a3ae0a1a9ae4fbc19d4c.png

其实可以扩展到更一般的形式:

397632dcd8585e7a2ebee4e7c1988f14.png

虽然平方探测法解决了线性探测法的一次聚集,但是它也有一个小问题,就是关键字key散列到同一位置后探测时的路径是一样的。

7bf849927aa38ab396bb5aecfefca48c.png

这样对于许多落在同一位置的关键字而言,越是后面插入的元素,探测的时间就越长。

这种现象被称作“二次聚集(secondary clustering)”,其实这个在线性探测法里也有。

这种现象出现的原因是由于对于落在同一个位置的关键字我们采取了一个依赖 i 的函数(i或者i^2)来进行探测,它不会因为关键字的不同或其他因素而改变探测的路径。那么我们是不是可以让探测的方法依赖于关键字呢?

0b3a6691a70ce23b524abcfcd63819d5.png

双散列

2c0c52f2fd183a17b6ee6b21b9542ebc.png

答案是可以的,我们可以再弄另外一个Hash函数,对落在同一个位置的关键字进行再次的Hash,探测的时候就用依赖这个Hash值去探测,比如我们可以使用下面的函数:

4ad8efc01a182dfa834c2012da4aa6dd.png

经过hash1的散列后,会定位到某一个地址,如果这个地址冲突,那么就按照1*hash2(key)、2*hash2(key)... 的偏移去探测合适的位置。

1820b2e198501d5341c5a2b3ece79452.png

由于Hash2函数不同于Hash1,所以两个不同的关键字Hash1值和Hash2值同时相同的概率就会变得非常低。

这样就避免了二次聚集,但同时也付出了计算另一个散列函数Hash2的代价。

如果hash2(key)=0,那探测不就一直在原地不动,失效了吗?

d8728d9e31fa2336b950ebaaf737a460.png 7978784058df1e02aa037741fc5ae63b.png

一尘

e061274030e1f65c11da76a60a8dbb21.png

慧能

0a6fe1a61b359b0f59e84fc91023f825.png

聪明,所以hash2函数在选择的时候要避免这种情况。

原来解决冲突还有这种方法,看来国庆后要加倍努力了。

d8728d9e31fa2336b950ebaaf737a460.png 7978784058df1e02aa037741fc5ae63b.png

一尘

e061274030e1f65c11da76a60a8dbb21.png

慧能

0a6fe1a61b359b0f59e84fc91023f825.png

嗯嗯,看好你

其他相关:

一个故事讲完哈希洪荒攻击

推荐阅读

全部文章详细分类与整理(算法+数据结构+计算机基础)

玩公众号写文章一年多以来,我经历了被喷被拉黑被赞美,我酸了

有必要说一说即将到来的春招(经历+重要性+如何准备)

普普通通,我的三年大学

历经两个月,我的秋招之路结束了!

0fbf2968e9a9040b1fde6d75f98fff85.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值