散列表是根据关键字直接进行访问的数据结构。散列表通过散列函数将关键字映射到存储地址,建立了关键字和存储地址之间的一种直接映射关系。
散列函数(Hash function),又称为哈希函数,是将关键字映射到存储地址的函数,相当于一种映射规则。
方便理解,下面举例两个常见的散列函数
直接定址法:直接取关键字的某个线性函数作为散列函数,散列函数形式如下:
hash(key)=a*key+b
例如,学生的学号{601001, 601002, 601005, …, 601045},那么可以设计散列函数为:H(key)=key-601000这样可以将学生的学号直接映射到存储地址下标,符合简单均匀的原则
除留余数法:除留余数法是一种最简单、最常用的构造散列函数的方法,并且不需要求事先知道关键字的分布。假定散列表的表长为m,取一个不大于表长的最大素数p,则设计散列函数为:hash(key)=key%p
处理冲突的方法
无论如何设计散列函数,都无法避免冲突问题。如果发生冲突,就需要进行冲突处理。冲突处理方法分为3种:开发地址法、链地址法、建立公共溢出区。
一、开放地址法
开放地址法是在线性存储空间上的解决方案,也称为闭散列
当发生冲突时,采用冲突处理方法在线性存储空间上探测其他的位置。
hash′(key)=(hash(key)+di)%m
其中,hash(key)为原散列函数,hash′(key)为探测函数,di为增量序列,m为表长。根据增量序列的不同,开放地址法又分为线性探测法、二次探测法等。
(1)线性探测法
线性探测法是最简单的开发地址法,线性探测的增量序列如下:
di=1, …, m-1
例如,一组关键字(14, 36, 42, 38, 40, 15, 19, 12, 51, 65, 34, 25),若表长为15,散列函数为hash(key)=key%13,采用线性探测法处理冲突,构造该散列表
图解:
按照关键字顺序,根据散列函数计算散列地址,如果该地址空间为空,则直接放入,如果该地址空间已存有数据,则采用线性探测法处理冲突
-
hash(14)=14%13=1,将14放入1号空间(下标为1)
-
hash(36)=36%13=10,将36放入10号空间
-
hash(42)=42%13=3,将42放入3号空间
-
hash(38)=38%13=12,将38放入12号空间
hash(40)=40%13=1,1号空间已存储数据,采用线性探测处理冲突:
hash′(40)=(hash(40)+di)%m, di=1, …,m-1