散列表
什么是散列表
"散列"的基本思想是:
1.以关键字key为自变量,通过一个确定的函数(散列函数),通过函数计算出确定的函数值,作为数据对象存储地址
2.可能不同散列对象会映射到一个散列地址上,则称之为冲突 ------ 需要某种解决策略
如上图:
我们依次读入数据时,对于同一个key值和散列函数,总有一些数据时会有一样的地址的,其中3和25的地址都是3,由于3先被读入,就导致25没有被记录到哈希表中,15和37也是一样的37也没有被记录到哈希表中;
这就形成了冲突地址,我们必须要解决这些冲突,以便每个数据都可以被存入哈希表中
散列函数构造
一个"好"的散列函数应该考虑两个因素
- 计算简单,以便提高转换速度
- 关键词对应的地址空间分布均匀,以尽量减少冲突
1.直接定址法
取关键字的某个线性函数值作为散列地址
如线性序列 n = { 1990,1991,1992,1993,1994,1995,1996,1997,1998,1999,2000}
散列地址:address = value - 1990
address = {0,1,2,3,4,5,6,7,8,9,10}
2.除留余数法(最常用)
定义一个 key值
使用序列值 取余 这个key值,最后的结果就是地址
address = value % key
注意:这里的key一般取素数,这样可以保证哈希表最小的冲突率
3.数字分析法
分析数字关键字在各位上的变化情况,取比较随机的位作为散列地址
如:手机号11位作为value值时,我们重庆的大部分都是前7位都是一样的,只有最后四位是校验位,所以是随机的
利用后四位来作为散列地址可以极大的减少冲突率
再如身份证号码:
如果是在同一地区的显然利用出生日期来作为散列地址比较适合,如果同龄人太多,也可以使用月份+日期来进行散列
还有身份证后四位,代表派出所代码+性别+校验码,重复率低,作为散列地址可以减少冲突率
4.折叠法
把关键词分为几个相同的部分,然后叠加
如:654237163
这是一个九位的关键字,可以彩粉为3的倍数来叠加:654,237,163
654+237+163 = 1054
其中1054太大了,如果数据不多可以使用54,或者4来作为散列地址,如果数据比较多或者很多,就可以使用 054 来作为散列地址
5.平方取中法
如数据 36542
36542^2^ = 36542 * 36542 = 1335317764
计算的结果是 10 位数字,我们就可以根据数据量来从中间选取散列地址
如果数据少,可以选择中间两位 31 或者数据很少 选择3作为散列地址都是可以的
当数据为字符串时:
1.一个简单的散列函数
直接使用ASCll码和法
利用每个字符的ASCll码相加求和最后的结果取余 key值,就可以得到散列地址
address = (Σvalue[ i ]) mod key
缺点:重复率高,字符一般是以字符串存在很可能有大量的重复
2.简单改进----前三位向前移位
addredd = (value[0] * 27^2^ + value[1] * 27^1^ + value[2] * 27^0^)
地址冲突后的解决方式
开放地址法
若发生了第i次冲突,试探的下一个地址将增加d~i~ 基本公式是:
address = (key + i)mod key
d~i~解决不同冲突的方案:
- 线性探测,每次增加i个,即d~i~ = i
- 平方探测,每次以 ± i^2^增加,此时会有两个方向,左方是 -i^2^,右方是 + i^2^
- 双散列,构造一个新的散列key在冲突的时候使用
线性探测:
ALS(平均查找次数) = (1 + 1 + 1 + 2 + 2 + 1)/ 6 = 1.33
平方探测:
分离链接法:
将相应位置上的冲突的所有关键字存储在同一个单链表中