[路飞]_哈希表与布隆过滤器

205 阅读6分钟

首先,哈希表用来解决什么问题?

哈希表是用来解决快速索引数据的问题。

首先来说一种我们非常熟悉的结构,数组结构,当给出下标以后,想取到下标中的这个值,时间复杂度是O(1),效率是非常高的,但是数组下标的类型是整形,在实际用程序去解决问题的过程中,用于索引的数据,可能比整形要复杂的多。

那么如何满足任意数据类型的索引需求呢,如果可以把任意数据映射成为一个数组下标,当完成了这种映射关系以后,那么往后的数据存取过程其实就是O(1)的数组的操作过程,这就是哈希表的整体思想。哈希表为了完成对于任意数据的高效的存取,最关键的一步就是将任意数据映射成为下标。这一步就是哈希函数干的事情。哈希函数干的事情就是从任意数据类型到数组下标的映射。任意数据的数据类型不一样,哈希函数的映射规则也不一样。

总结:哈希表,其实想利用到数组快速存取数据的这个特性。要是想利用这个特性,就需要完成最关键的一部分。就是将任意数据映射转换成数组下标,既哈希函数完成的事情。

哈希操作完成的是从高维空间到低维空间的映射,比如:低维空间,就像电影院的座位一样,是有限的。而高位空间像什么,高位空间,就像我们的这个人群一样,人的数量一定会大于电影院的这个座位数量。这个时候就涉及一个问题。就是两个人想要做同一个座位。那么这种情况呢就是哈希冲突。

在哈希表中低维数据就是数组下标,在具体的例子来看,假设呢有一个数据16,哈希函数值这么设计,是用当前数值对于数组大小取余。再假设数组大小是9,计算16%9=7,就可以把16这个数字放到7这个位置。这就是哈希函数所起到的这种映射作用,还是这个哈希函数。那么接下来有一个数字7。按照当前函数,会把7映射成为7,那么数字7和数字16都准备放到一个格子中,这个时候,就产生了哈希冲突。

当设计哈希表的时候,永远不要想没有哈希冲突,只能解决冲突。哈希表中重要的两个组成部分。第一个组成部分,叫做哈西函数,哈希函数是用来计算得到初步的下标值,然后还有一个必须要设计的过程,就是说当哈希产生冲突时哈希冲突的处理方法。

面对哈希冲突前人总结四种处理冲突的方法:

  1. 开放定址法
  2. 再哈希法
  3. 建立公共溢出区
  4. 链式地址法(拉链法)

1.开放定址法。思想非常简单当前的这个值通过哈希函数,映射到了一个已经被占上了这个位置。那么接下来,再重新算一个位置就行了,假设从当前被占的这个位置(假设是7),依次往后去判断,第一次从7这个位置判断8这个位置,如果被占用。然后第二次再判断9这个位置,如果9这个位置仍然被占上,那就判断10那个位置了。那么这一种叫做线性探测法。这类方法就是开放定址法,其实它也有不同的方法。开放定址法,就是这一大类的方法,它的思想很简单,简单来说,在已有计算出的这个下标上,通过某种规则再计算,那么得到下面的一个下标。每次用当前发生冲突的下标,每次往后加一,这叫做线性探测法。除了线性探测法外,在开放定址法中,经常用的方法是二次再散裂。这个方法二次探测是这么加的,7加上1的平方,得8,第二次再去探测的时候是加上2的平方,就得到12,12再往后去进行探测,加3的平方,得到21,21再往后探测的时候是加上4的平方,开放定值法就是这一大类的方法。核心思想是说,当得到一个冲突的下标以后,再通过某些阶段规则向后去探测不是冲突的这个下标位置?这个计算规则是很灵活的。哈希表不是一个死记硬背的结构,是一个设计感极强的结构。

当设计一个哈希表的时候,第一考虑哈希函数怎么设计,不同的数据哈希函数的设计方式不一样,即使是相同类型的数据,哈希函数也有不同的设计方法。然后有哈希函数以后,还需要有相关的冲突处理方法。冲突处理方法也是需要进行设计的。

2.第二种冲突处理方法再哈西法,就是不只设计一种哈希函数。假如设计了三种哈希函数,首先数据在第一个哈希函数算出一个下标值。如果发生冲突了,就把当前数据再扔给第二个哈希函数。再让第二个哈希函数再去算下标,如果第二个哈希函数也发生冲突了,就发给第三个哈希函数。这就是再哈希法,再哈希法治标不治本因为有可能涉及到这些哈希函数啊,全都发生冲突了,不建议使用。一般情况下再哈希法需要配合其他的冲突处理方式去做一个兜底方案,意味是说当一组哈希函数全都发生冲突以后,这个时候就需要有一种冲突处理的兜底方案。

3.建立公共溢出区。当前这种场景,7和16发生冲突,建立一个公共溢出区,比如溢出区可以用红黑树维护。当7要存储的位置已经被占,就把7放到公共溢出区中。那么简单来说,就是所有发生冲突的数据,都往这个公共溢出区中去存放。公共溢出区用另外的一种数据结构进行维护。

4.链式地址法(拉链法),也叫拉链法,链就是链表的意思。拉链法的处理过程,非常简单粗暴。现在7和16,挤了一个位置,挤不下了,这时哈希表的每个位置,想象成一个链表的头节点。就是说16也想往这个位置存7也想往这个位置存。这个地方就不是一个单一的元素了,而是一条链表。拉链法,哈希表的每个位置存储的是一个链表的头节点。