首先是哈希表的优点:
-
无论数据有多少,处理起来都特别的快
-
能够快速地进行
插入修改元素
、删除元素
、查找元素
等操作 -
代码简单(其实只需要把哈希函数写好,之后的代码就很简单了)
然后再来讲讲哈希表的缺点:
-
哈希表中的数据是没有顺序的
-
数据不允许重复
=============================================================
前面提到了冲突,其含义就是在哈希化以后有几个元素的下标值相同,这就叫做 冲突。 那当两个元素的下标值冲突时,是后一个元素是不是要替换掉前一个元素呢?当然不是!
那么如何解决冲突这个现象呢?一般是有两种方法,即拉链法(链地址法) 和 开放地址法
这种方法是很常用的解决冲突的方法。我们还是拿上面那个例子来说,10本图书通过哈希化以后存入到长度为10的数组当中,难免有几本书的下标值是相同的,那么我们可以将这两个下标值相同的元素存入到一个单独的数组中,然后将该数组存放在他们原本所在数组的下标位置,如图
假设图书1
和 图书2
哈希化后的下标值都为3,那么我们就可以在原数组下标3的位置放一个数组,同时存放这两本图书。因此,无论是查询哪本图书,计算机获得的下标值都是3,然后再对这个位置的数组进行遍历即可获得想要的图书。
这是第一种解决 冲突 的方法,但使用是还是需要考虑数组长度是否合适的,之后会进行讲解。
这种方法简单来说就是当元素下标值发生冲突时,寻找空白的位置插入数据。假设当前下标值为1和3的位置分别已经插入了 图书3
和 图书5
,这时将 图书6
进行哈希化,发现它的下标值也是1,此时与 图书3
发生冲突,那么此时 图书6
就可以找到下一个空着的没插入元素的位置进行插入,如图
其实当发生冲突时,寻找空白的位置也有三种方法,分别是 线性探测 、二次探测 、再哈希法
1. 线性探测
顾名思义,线性探测的意思就是,当某两个元素发生冲突时,将当前索引+1,查看该位置是否为空,是的话就插入数据,否则就继续将索引+1,以此类推……直到插入数据位置。
但这种方法有一个缺点,那就是当数组中连续很长的一个片段都已经插入了数据,此时用线性探测就显得效率没那么高了,因为每次探测的步长都为1,所以这段都已经插入了数据的片段都得进行探测一次,这种现象叫做 聚集。如下图,就是一个典型的聚集现象
图书8
的下标值为1,与 图书3
冲突,然后进行线性探测,依次经过 图书6、5、1、7
都没有发现有空白位置可以插入,直到末尾才找到空白位置插入,这样挺不好的,所以我们可以选用 二次探测 来缓解 聚集 这种现象。
2. 二次探测
二次探测 在线性探测的基础上,将每次探测的步长改为了当前下标值 index + 1²
、index + 2²
、 index + 3²
…… 直到找到空白位置插入元素为止
还是举一个例子来理解一下 二次探测 吧
假如现在已存入 图书3
、图书5
、图书7
,如图
然后此时要存入一个 图书6
,通过哈希化以后求得的下标值为2,与 图书5
冲突了,所以就从索引2的位置向后再移动 1²
个位置,但此时该位置上已存有数据,如下面这个动图演示
所以此时从索引为2的位置向后移动 2²
个位置,此时发现移动后的位置上也已存有数据,所以仍无法插入数据,如下面这个动图演示
因此,我们继续从索引2的位置向后移动 3²
个位置,此时发现,移动后的位置上有空余位置,于是直接在此插入数据,这样一个二次探测的过程就完成了,如下列动图演示
我们可以看到,二次探测 在一定程度上解决了 线性探测 造成的 聚集 问题,但是它却在另一种程度造成了一种聚集,就比如 1²
、2²
、3²
…… n²
上的聚集。所以这种方式还是有点不太好。
3. 再哈希法
再哈希法 就是再将我们传入的值进行一次 哈希化,获得一个新的探测步数 step
,然后按照这个步数进行探测,找到第一个空着的位置插入数据。这在很大的程度上解决了 聚集 的问题。
既然要再进行哈希化获得一个探测的步数,那么这个哈希化的处理过程一定要跟第一次哈希化的处理过程不一样,这样才能确认一个合适的搜索步长,提高查找效率。
这里,我们就不用担心如何写一个不一样的哈希函数了,给大家看一个公认的比较好的哈希函数:step = constant - (key % constant)
其中,constant
是一个自己定的质数常量,且小于数组的容量; key
就是第一次哈希化得到得值。
然后我们再通过这个函数算得的步长来进行查找搜索空位置进行插入即可,这里就不做过多的演示了。
====================================================================
在了解哈希表的扩容之前,我们来了解一个概念,叫做填充因子,它表示的是哈希表中的数据个数与哈希表长度的比值。其决定了哈希表的存取数据所需的时间大小。
当我们用第一种解决冲突