**引入:**咱们在学习数组的时候,使用数组元素的下标值便可访问到该元素,所花费的时间是O(1),与数组元素的个数n没有关系,这就是哈希方法的核心思想。golang
**哈希方法:**以关键值K为自变量,经过必定的函数关系h(K)(哈希函数)计算出对应的函数值,把这个值解释为结点的存储地址,将结点的关键码(key)和属性数据(value)一块儿存入此存储单元中。检索时,用一样的函数计算出地址,找到对应的数据。算法
**哈希表:**按哈希存储方式构造的存储结构称为哈希表(hash table)数组
**举例:**已知线性表关键码值集合为S={and, begin, do, else, for,golang},假设哈希函数是关键码K的第一个字母在字母表中的序号,哪么能够创建以下的哈希表:数据结构
固然这个例子特别简单,细心的同窗很快就发现了二个问题:app
其实这就是接下来要谈到的哈希函数的选择和冲突解决策略。函数
哈希方法的核心就是哈希函数的选择,理想的哈希函数应该使得结点“分布均匀”,且冲突少学习
为了简单起见,如下的哈希函数咱们假设关键值都是整数(若是不是整数,哪也有特定的方法能够把它转换为整数,毕竟在计算机世界里,任何东西都是01组成的串)优化
除余法:spa
关键值码k除以M(每每取哈希表的长度),并取余数做为哈希地址。blog
乘余取整法
先让关键码k乘上一个常数A(0<A<1),提取乘积的小数部分。而后,再用整数n乘以这个值,对结果向下取整,将其做为哈希的地址。
有许多种哈希函数能够选择,每种都有其适用的场景,可是做为软件开发工程师,咱们只要理解它的思想就能够,至于什么场景选择什么哈希函数,哪大几率是数学家应该研究的问题。
优秀的哈希函数能够尽量的避免产生冲突,可是冲突的产生是不可避免地,这就是接下来要谈到的冲突解决策略
冲突解决技术能够分为两类,拉链法和开地址法,这两种方法的不一样之处在于,拉链法把发生冲突的关键码存储在哈希表主表以外,而开地址法把发生冲突的关键码存储在表中另外一个槽内。
拉链法的一种简单形式是把哈希表中的每个槽定义为一个链表的表头,哈希到一个特定槽的全部记录都放到这个槽的链表中。
假设S={and, apple, begin, do, dog, else, for, go, golang},哈希函数仍是关键码K的第一个字母在字母表中的序号。哪么能够创建以下的哈希表:
开地址法把全部记录直接存储在哈希表中。每一个记录关键码K有一个由哈希函数计算出来的基位置,即h(k)。若是要插入一个关键码k,而另外一个记录的关键码已经占据了k的基位置(发生冲突)则把k存储在表中的其余地址内,至于其余地址怎么选择,有多种算法,咱们以顺序探测法为例。
已知一组关键码为{26,36,41,38,44,15,68,12,06,51,25},哈希表长度L=15,用线性探查法解决冲突构造的哈希表的过程以下:
利用除余数做为哈希函数,假设选择M=13,则散列函数为:h(k)=k%13,按顺序插入各个结点:
h(26)=0, h(36)=10,h(41)=2, h(38)=12, h(44)=5,
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
26 | 25 | 41 | 44 | 36 | 38 |
h(15)=0,发生碰撞,所以须要进行探查,按照顺序探测法,显然3为开放的空闲地址,所以能够将其放在3单元。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
26 | 25 | 41 | 15 | 44 | 36 | 38 |
68和12也相似,最后的哈希表以下所示:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
26 | 25 | 41 | 15 | 68 | 44 | 6 | 36 | 38 | 12 | 51 |
本文简述了哈希表的基本概念和思想,做为软件工程师,掌握这些知识足够咱们从事工程开发了,至于一些偏数学方面的知识,好比散列方法的效率分析,以及相关的一些优化,本文就再也不讲述。
参考资料:《数据结构与算法》张铭 编著