散列表定义
散列表,又称哈希表,hash表。散列表是一种特殊的数据结构,它同数组、链表以及二叉排序树等相比较有很明显的区别,它能
够快速定位到想要查找的记录,而不是与表中存在的记录的关键字进行比较来进行查找。这个源于散列表设计的特殊性,它采用
了函数映射的思想将记录的存储位置与记录的关键字关联起来,从而能够很快速地进行查找。
设计思想
Hash表采用一个映射函数 f : key —> address 将关键字映射到该记录在表中的存储位置,从而在想要查找该记录时,可以直接根
据关键字和映射关系计算出该记录在表中的存储位置,通常情况下,这种映射关系称作为Hash函数,而通过Hash函数和关键字
计算出来的存储位置(注意这里的存储位置只是表中的存储位置,并不是实际的物理地址)称作为Hash地址。
HASH函数
- 直接定址法
取关键字或者关键字的某个线性函数为Hash地址,即Hash(key)=a*key+b
- 除留余数法
如果知道Hash表的最大长度为m,可以取不大于m的最大质数p,然后对关键字进行取余运算,Hash(key)=key%p。
在这里p的选取非常关键,p选择的好的话,能够最大程度地减少冲突,p一般取不大于m的最大质数。
- 平方取中法
对关键字进行平方运算,然后取结果的中间几位作为Hash地址。假如有以下关键字序列{421,423,436},平方之后的结果为{177241,178929,190096},那么可以取{72,89,00}作为Hash地址。
- 折叠法
将关键字拆分成几部分,然后将这几部分组合在一起,以特定的方式进行转化形成Hash地址。假如知道图书的ISBN号为8903-241-23,可以将Hash(key)=89+03+24+12+3作为Hash地址。
冲突解决
无论利用上述哪种hash函数计算hash地址,难免会产生不同元素的hash地址一样,那么这样就产生了冲突,那么如何避免冲突
是一件很关键的事。下面有两种方式解决冲突:开放定址法与分离链接法(链地址法)。由于篇幅问题,这篇博文主要介绍开放
定址法
- 开放定址法
当一个关键字和另一个关键字发生冲突时,使用某种探测技术在Hash表中形成一个探测序列,然后沿着这个探测序列依次查找下
去,当碰到一个空的单元时,则插入其中。基本公式为:hash(key) = (hash(key)+di)mod TableSize。其中di为增量序列,
TableSize为表长。根据di的不同我们又可以分为线性探测,平方(二次)探测,双散列探测。
- 线性探测
以增量序列 1,2,……,(TableSize -1)循环试探下一个存储地址,即di = i。如果table[index+di]为空则进行插入,反之试探下一个增量。但是线性探测也有弊端,就是会造成元素聚集现象,降低查找效率。
特别对于开放定址法的删除操作,不能简单的进行物理删除,因为对于同义词来说,这个地址可能在其查找路径上,若物理删除的话,会中断查找路径,故只能设置删除标志。
- 平方探测
以增量序列1,-1,4,-4…且q ≤ TableSize/2 循环试探下一个存储地址。
- 双散列探测
di 为i*h2(key),h2(key)是另一个散列函数。探测序列成:h2(key),2h2(key),3h2(key),……。对任意的key,h2(key) ≠ 0 !探测序列还应该保证所有的散列存储单元都应该能够被探测到。选择以下形式有良好的效果:
h2(key) = p - (key mod p)
其中:p < TableSize,p、TableSize都是素数。
原文:https://blog.csdn.net/qq_30091945/article/details/78044445