散列

百般思念 提交于 2020-01-28 21:43:03

什么是哈希表?
原文链接:https://blog.csdn.net/yyyljw/article/details/80903391

哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
哈希表hashtable(key,value) 就是把Key通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。(或者:把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。)

数组的特点是:寻址容易,插入和删除困难;
而链表的特点是:寻址困难,插入和删除容易。

那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表,哈希表有多种不同的实现方法,我接下来解释的是最常用的一种方法——拉链法,我们可以理解为“链表的数组”,如图
在这里插入图片描述
左边很明显是个数组,数组的每个成员包括一个指针,指向一个链表的头,当然这个链表可能为空,也可能元素很多。我们根据元素的一些特征把元素分配到不同的链表中去,也是根据这些特征,找到正确的链表,再从链表中找出这个元素。

缺点:它是基于数组的,数组创建后难于扩展,某些哈希表被基本填满时,性能下降得非常严重,所以程序员必须要清楚表中将要存储多少数据(或者准备好定期地把数据转移到更大的哈希表中,这是个费时的过程)。

散列冲突的解决方案:

1.建立一个缓冲区,把凡是拼音重复的人放到缓冲区中。当我通过名字查找人时,发现找的不对,就在缓冲区里找。

2.进行再探测。就是在其他地方查找。探测的方法也可以有很多种。

(1)在找到查找位置的index的index-1,index+1位置查找,index-2,index+2查找,依次类推。这种方法称为线性再探测。

(2)在查找位置index周围随机的查找。称为随机在探测。

(3)再哈希。就是当冲突时,采用另外一种映射方式来查找。
扩展
d-left hashing中的d是多个的意思,我们先简化这个问题,看一看2-left hashing。2-left hashing指的是将一个哈希表分成长度相等的两半,分别叫做T1和T2,给T1和T2分别配备一个哈希函数,h1和h2。在存储一个新的key时,同 时用两个哈希函数进行计算,得出两个地址h1[key]和h2[key]。这时需要检查T1中的h1[key]位置和T2中的h2[key]位置,哪一个 位置已经存储的(有碰撞的)key比较多,然后将新key存储在负载少的位置。如果两边一样多,比如两个位置都为空或者都存储了一个key,就把新key 存储在左边的T1子表中,2-left也由此而来。在查找一个key时,必须进行两次hash,同时查找两个位置。

不使用链表的散列表

1.线性探测法

当我们要往哈希表中插入一个数据时,通过哈希函数计算该值的哈希地址,当我们找到哈希地址时却发现该位置已经被别的数据插入了,那么此时我们就找紧跟着这一位置的下一个位置,看是否能够插入,如果能则插入,不能则继续探测紧跟着当前位置的下一个位置。

2.平方探测的原理

探测的过程中(即 Hashval + i * i) Hashval是不变的,直至找到合适位置后,重新给Hashval赋值。
①根据哈希函数算出Hashval,i 初始化为零,判断HashTable[Hashval]是否被占用,如果没被占用,则Hashval就是根据哈希函数算出的值,跳出平方探测;如果被占用则向右探测(执行②);

②判断HashTable[Hashval + i * i]是否被占用,如果没被占用,则Hashval = Hashval + i * i,跳出平方探测;如果被占用则向左探测(执行③);

③判断HashTable[Hashval - i * i]是否被占用,如果没被占用,则Hashval = Hashval - i * i,跳出平方探测;如果被占用则i++继续向右探测(执行②);

3.双散列

双重散列是线性开型寻址散列(开放寻址法)中的冲突解决技术。双重散列使用在发生冲突时将第二个散列函数应用于键的想法。

(hash1(key) + i * hash2(key)) % TABLE_SIZE

来进行双哈希处理。hash1() 和 hash2() 是哈希函数,而 TABLE_SIZE 是哈希表的大小。当发生碰撞时,我们通过重复增加 步长i 来寻找键。

再散列

如果散列表太满,操作运行时间过长,且插入操作可能失败,解决方法是珈百璃另一个大约两倍大的表,而且使用另一个相关的新散列函数,扫描整个原始散列表,计算每个未删除的元素的新散列值并将其插入到新表中。
一种做法是当表满到一半时就再散列,另一种当插入失败时才散列,第三种途中策略:当散列表达到某一装填因子时再散列。

布谷鸟散列
链接:https://www.jianshu.com/p/68220564f341

算法描述:使用hashA、hashB计算对应的key位置
1、两个位置均为空,则任选一个插入;
2、两个位置中一个为空,则插入到空的那个位置
3、两个位置均不为空,则踢出一个位置后插入,被踢出的对调用该算法,再执行该算法找其另一个位置,循环直到插入成功。
4、如果被踢出的次数达到一定的阈值,则认为hash表已满,并进行重新哈希rehash

跳房子散列

跳房子散列的思路:用事先确定的,对计算机底层体系结构而言最优的一个常数,给探测序列的最大长度加个上界。这样做可以给出常数级的最坏查询时间

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!