数据结构精讲:从原理到实战–学习笔记05

数据结构精讲:从原理到实战–学习笔记05

本笔记是记录学习 《数据结构精讲:从原理到实战》,作者是:蔡元楠,Google Brain资深工程师。

如有侵权,联系删除!

哈希碰撞

在概念上哈希表可以定义为是一个根据键(Key) 而直接访问在内存中存储位置的值(Value) 的数据结构。这里所说的键就是指哈希函数的输入,而这里的值并不是指哈希值,而是指我们想要保存的值。

有时同样的哈希key输入,会得到相同的值value,这就称为哈希碰撞。

开放寻址法(Open Addressing)

开放寻址法本质上是在数组中寻找一个还未被使用的位置,将新的值插入。这样做的好处是利用数组原本的空间而不用开辟额外的空间来保存值。最简单明了的方法就是沿着数组索引,往下一个一个地去寻找还未被使用的空间,这种方法叫做线性探测(Linear Probing)

一般哈希表并不会等到这个数组满了之后再进行扩容,其实底层数据结构会维护一个叫做负载因子(Load Factor) 的数据,负载因子可以被定义为是哈希表中保存的元素个数 / 哈希表中底层数组的大小。当这个负载因子过大时,则表示哈希表底层数组所保存的元素已经很多了,所剩下的未被使用过的数组位置会很少,同时产生哈希碰撞的概率会很高,这并不是我们想看到的。所以一般来说,在负载因子的值超过一定值的时候,底层的数组就得需要进行扩容了,像在 Java 的 JDK 中,都会把负载因子的默认值设为 0.75 (源码地址)。

采用这种线性探测的好处是算法非常简单,但它也有自身的缺点。因为每次遇到哈希碰撞的时候都只是往下一个元素地址检测是否有未被使用的位置存在,所以有可能会导致一种叫做哈希聚集(Primary Clustering) 的现象。(大量元素聚合在一起,并没有均匀的分布在哈希表中)

对此有一些改进的算法被提出用以缓解上面所说的哈希聚集问题,比如平方探测(Quadratic Probing)二度哈希(Double Hashing)

  • 平方探测指的是每次检查空闲位置的步数为平方的倍数。例如说,当新元素插入的键所产生的哈希值为 i,那下一次的检测位置为:i 加上 1 的平方、i 减去 1 的平方、i 加上 2 的平方、i 减去 2 的平方、…,以此类推。

  • 二度哈希指的是数据结构底层会保存多个哈希函数,当使用第一个哈希函数算出的哈希值产生了哈希碰撞之后,将会使用第二个哈希函数去运算哈希值,…,以此类推。

分离链接法(Separate Chaining)

分离链接法与链表有很大的关系,它的本质是将所有的同一哈希值的键值对都保存在一个链表中,而哈希表底层的数组元素就是保存这个哈希值对应的链表。

采用分离链接法会使用到额外的空间来保存新插入的键值对。虽然这里举的例子采用的是链表,这在查找一个键对应的值时,有可能时间复杂度会降级为 O(N),但很多时候我们可以进一步将存储结构优化成红黑树,在 Java JDK 中的 HashMap 类其实就是使用了分离链接法。

Bloom Filter

Bloom Filter 是一个哈希表和位数组相结合的基于概率的数据结构 ,由 Bloom 在 1970 年提出。它主要用于在超大的数据集合中判断一个元素是否存在这个集合中,像判断一个人是否在黑名单中一样,或者判断一封邮件是否属于垃圾邮件的范畴等等。因为使用了位数组,所以使得 Bloom Filter 在空间利用率上非常的高效。而同时它的底层原理和哈希表一致,使得它在查找的时间复杂度上也十分优秀。为什么说它是基于概率的数据结构呢?下面来看看它的原理你就明白了。

Bloom Filter 的原理是将一个元素通过多个哈希函数映射到位数组中。我们以黑名单为例来说明,假设哈希函数的个数为 2,也假设黑名单中只有 a 和 b 两个元素,在通过两次哈希函数映射到位数组之后,内存结构图如下图所示:

在这里插入图片描述

我们把元素经过两次哈希函数之后所对应的哈希值的位置设为 1,此时需要判断元素 c 是否在黑名单中,需要将 c 也进行两次哈希函数运算,得到的结果如下图所示:

在这里插入图片描述

你会发现 c 在经过哈希函数映射之后有一个哈希值对应的位置结果为 0,那就表示 c 这个元素一定不在黑名单中。此时我们再来还需要判断元素 d 是否在黑名单中,需要将 d 也进行两次哈希函数运算,得到的结果如下图所示:

在这里插入图片描述

你会发现 d 在经过哈希函数映射之后有两个哈希值所对应的位置结果都为 1,但是我们只能判断 d 这个元素有可能在黑名单中。所以这里存在一个误判率,也就是说即便经过 N 个哈希函数之后哈希值对应位置的结果都为 1 了,但这个元素不一定真的存在集合中。

误判率的公式如下:

在这里插入图片描述
其中,m 表示位数组里位的个数,n 表示已经存储在集合里的元素个数,k 表示哈希函数的个数。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值