《数据结构与算法》之散列表

散列表

什么是散列表

"散列"的基本思想是:

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

平方探测:

分离链接法:

将相应位置上的冲突的所有关键字存储在同一个单链表中