数据结构 - 哈希策略和冲突处理

1. 哈希策略

访问集合中的项的最快的方法,是通过数组和基于数组的列表所支持的随机访问。
假设第一个建是1500,剩下的键是连续性的数字。数组中一个给定的位置,可以通过表达式key - 1500 来计算。这种类型的计算叫做从键到地址的转换,或者称为哈希函数。哈希函数在一个给定的键上执行,并且返回其在数组中的相对位置。使用一种哈希策略的数组,叫做哈希表
如果哈希函数以常量时间运行,插入、访问和删除相关联的键都是O(1)。

某些哈希函数对不同的键会产生同一个索引,比如哈希函数key % 4 对于键3、5、8、10都会产生唯一索引。但对于健3、4、8、10不能找到唯一的位置。因为,4 和 8 有相同的索引。
像键4 和 8 得到相同的索引,称为冲突。

合适的哈希策略应该由两部分组成:哈希函数 和 冲突处理。

2. 冲突和密度的关系

和第一点中同一个实例,当数组长度为4 时 对3、4、8、10发生冲突。但,若是将数组长度提升为8 时,不再发生冲突。
换句话说,随着密度的降低,冲突的可能性也会减少。但随之增加的是内存的消耗。
装载因子(项的数目除以数组的长度)超过0.5时,发生冲突的可能性是很高的,0.2的装载因子是一个避免冲突的好办法,但会导致巨大的内存成本。

3. 非数字键的哈希

上面的例子键都是整数,当然也可以示字符串。
你可能会尝试返回字符串中的ASCII 值的加和。然而,这种方法对回文字符串、顺序不同的字符串依然会返回相同的键。
另一个问题是,英语中单词的首字符分布并不均匀,比如a、b开头的必然比y、z开头的多。这可能导致由此产生的加和不平衡或偏移,从而导致键的分布集中在整个键集的某一段范围之内。
这种聚簇反过来导致数组中的键的聚簇,而理想情况下,键最好是平均分布于数组之中。为了减少首字母造成的潜在偏移,并且减少回文导致的效果,如果字符串的长度大于某一个阈值,可以将首字母从字符串中丢弃,然后再计算加和。此外,如果字符串超过了某一个长度,可以减去最后一个字符的ASCII值。
不管哈希函数多么高级,哈希表还是可能冲突。下面介绍一些常用的解决办法。

4. 线性探测

对于插入来说,解决冲突最简单的方式,就是从冲突位置开始搜索数组,找到第1个可能的位置,这个过程叫做线性探测。

比如,以 数组长度为6 为例,初始状态数组为:
在这里插入图片描述
再向数组插入13 时,13 % 6 = 1,发生冲突。
通过线性探测,向右寻找可用的单元格:
从下标 1 + 1 =2,下标2 已被占用,继续向右 2 + 1 = 3,下标3未被占用,则插入,插入后的状态为:
在这里插入图片描述
访问和删除的工作方式是类似的。
对于访问来说,当前的数组单元格为空或者它包含目标项的时候就停止探测。这使得你能够跳过之前占用的单元格以及当前占用的单元格。
对于删除来说,我们也是和访问一样探测。如果找到了目标项,就将单元格的值设置为空。

但是,这种解决冲突的方法也会造成两个问题:

  1. 在几次插入和删除之后,一些标记为DELETED的单元格可能位于给定的项和其主索引之间。意味着,该项距离其主索引比实际所需的位置要远,因此,增加了平均的整体访问时间。
  2. 容易导致聚簇。当发生冲突时,会向右探测可用的单元格(意味着连续),很有可能所有的数据全部集中在某一个部分。 – 解决该问题使用二次探测

5. 二次探测

二次探测通过将主索引增加每一次尝试的距离的平方,如果失败了,再次增加距离并尝试。
所以使用的公式是:(k +(-) d^2) % m, d = 1,2,3…m/2-1
例如:和上一个示例一样,初始状态如下
在这里插入图片描述
二次探测步骤如下:同样插入13
k = 13 % 6 = 1

index = (1 + 1^2) % 6 = 2,单元格 2 被占用,继续探测
index = (1 - 1^2) % 6 = 0,单元格 0 未被占用,停止探测,插入13
如果仍然被占用,步骤应该如下:
index = (1 + 2^2) % 6 = 5
index = (1 - 2^2) % 6 = 3
… …

插入后的状态为:
在这里插入图片描述

5. 链化

另一种解决冲突的方法叫做链化。项都存储在链表或链的一个数组中。每一项的键都会找到该项已经插入到链中的桶或索引。

访问和删除操作每次都会执行如下步骤:

  1. 计算项在数组中的主索引
  2. 搜索该项在链表中的索引

如果找到了该项,返回或删除它。

要插入一个项,执行如下步骤:

  1. 计算项在数组中的主索引
  2. 如果数组单元格为空,创建一个节点,它带有该项,并且将该节点赋值给单元格,否则产生冲突。在该位置已有的项的头部插入新的项。

同样以上面的例子为例:
初始状态如下:(左侧0-5 可以为一个数组,存放链表的表头)
在这里插入图片描述
插入13,主索引 = 13 % 6 = 1,插入后状态:
在这里插入图片描述

完!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值