在使用类似链表、数组以及栈和队列等数据结构处理问题时,小九往往会被一些复杂度的问题弄得脑壳疼,所以今天我们来了解一下一个复杂度问题极其理想的结构:哈希表。
哈希表的概念:
在之前学习过的顺序结构以及平衡树中,元素关键码和存储位置没有对应的关系,在查找时需要进行多次比较才能找到正确的位置。
但是在哈希问题中,主要实现的规则就是:可以不经过任何比较,一次性的从表中得到我们要搜索的元素。
那么,想实现这种功能,就要通过某种方法让关键码和元素存储位置之间能够建立一一映射的关系。而这种方法我们乘坐哈希函数。
此时插入元素就要将关键码通过该函数计算出元素的存储位置来存放元素。
而搜索某元素是要用对元素关键码用同样的方式进行计算,然后与存储结构中的位置元素比较,如果相等则搜索成功。
这样的方式就叫做哈希(散列)方法,构造出来的存储结构就是我们所说的哈希表(散列表)。
哈希问题中的冲突
不同的关键字通过同一个哈希函数计算出来的地址可能会相同,这种现象就是哈希冲突,或者也可以叫做哈希碰撞。
哈希问题中是通过哈希函数计算出来的地址进行存储,当哈希冲突发生时,就会出现两个关键字的地址同时指向一个地址。
哈希冲突的避免
在日常应用中,哈希表底层数组的存储容量往往是小于实际存储的元素数量的,因此我们可以认为,哈希冲突是不可避免的,我们只能尽量的降低哈希冲突的概率。
哈希函数的设计
引起哈希冲突的原因很可能是哈希函数的设计不合理,才导致计算出来的哈希地址大面积相同,因此降低哈希函数的冲突率我们可以从设计哈希函数入手。
(1)哈希函数的设计规则:
①哈希函数的定义域要包含所需要存储的所有元素。
②通过哈希函数计算出来的地址要能够均匀的分布在整个空间中。
③哈希函数应该是比较简单的。
(2)一些常见的哈希常数
①直接定制法
这种哈希函数实际上是一个线性函数:Hash(Key) = A * Key + B;
②除留余数法
这种方法是取一个不大于地址数但接近地址数的质数为除数,按照:Hash(Key) = Key % (该质数)的函数将关键码转换成哈希地址。
③平方取中法
抽取关键字平方值的中间三位数作为哈希地址。这种方式适合于不知道关键字的分布,并且关键字位数不大的情况。
④折叠法
将关键字从左到右分割成位数相等的几个部分,然后将这几部分叠加求和,最后对比散列表的长度,取后几位作为地址。
⑤随机数法
这种方法是接住随机函数取到随机数值作为哈希地址,当关键字长度不等时采用这种方法。函数:Hash(Key) = random(Key).
⑥数学分析法
负载因子调节
在哈希表中,有一个用来描述哈希冲突可能性的标志因子,α = 存入表中的元素个数 / 哈希表的长度。
其中α就叫做负载因子,当负载因子越大,产生哈希冲突的可能性越大,负载因子越小,产生哈希冲突的可能性就越小。
Java的系统库限制了负载因子数为0.75
哈希冲突的解决方法
前面我们说了哈希冲突是无法完全避免的,那么接下来,我们再看看对于已经出现的冲突我们该如何解决。
闭散列
闭散列也叫做开放定址法,开放定址法的 解决规则是:当发生哈希冲突时,如果哈希表还没有被装满,那么此时的哈希表肯定是有空余位置的,那么我们要继续存储已经出现冲突的关键字,就要找到下一个空余位置,而开放定址法为我们提供了两种寻找“下一个位置”的方式。
线性探测法
线性探测法,顾名思义,就是从发生冲突的位置往后依次进行探测,将发生冲突的元素存储到下一个空位置。
插入
1.通过哈希函数计算带插入元素在哈希表中的位置
2.如果计算出的位置已有元素(产生哈希冲突),依次往后寻找空位置插入新元素。
删除
闭散列处理哈希冲突时不能随便删除元素,如果直接删除可能会影响到其他元素的搜索。
二次探测法
二次探测法寻找空位置是用函数进行二次查找,方法:
①H(i) = (Key + i^2) % m
②H(i) = (Key - i^2) % m
如果两个函数计算出来的哈希地址依然发生冲突,那么i++,继续进行探测。
开散列
开散列法,又被称作链地址法,与上述探测法不同的是,它是将所有通过哈希函数计算出来的哈希地址相同的元素放置在同一个链表中,而它们的地址位置存储的是链表的头结点。
例:数据集合为{1,7,6,11,13,15,23,25,5};
哈希函数设置为:Hash(key) = key % capacity.
使用链地址法存储步骤为:
①1 % 10 = 1;
②7 % 10 =7;
③6 % 10 = 6;
④11 % 10 = 1;
⑤13 % 10 = 3;
⑥15 % 10 = 5;
⑦23 % 10 = 3;
⑧25 % 10 = 5;
⑨5 % 10 = 5

哈希表的性能分析
虽然我们认为哈希表的冲突问题很容易发生,但是在实际应用中,哈希表冲突率并不高,并且冲突个数是可控的,因此通常情况下,我们认为哈希表的插入、删除和查找的时间复杂度是O(1).
2968

被折叠的 条评论
为什么被折叠?



