散列表
哈希表
哈希函数+解决冲突的方法
构造方法
直接定址法,除留余数法
解决冲突的方法
开放定址法:线性探测法,平方探测法
拉链法
1、散列表的若干术语
散列方法(杂凑法)
选取某个函数,依该函数按关键字计算元素的存储位置,并按此存放;
查找时,由同一个函数对给定值k计算地址,将k与地址单元中元素关键码进行比对,确定查找是否成功
散列函数(杂凑函数)
散列方法中使用的转换函数
散列表(杂凑表)
散列函数:H ( key ) = k
冲突
不同的关键码映射到同一个散列地址在散列查找方法中,冲突是不可能避免的,只有尽可能的减少
关键字不同,哈希函数值相同
key1 != key2 , 但是H(key1) = H(key2)
堆积
堆积也称非同一次冲突,就是哈希函数值不相同的两个元素争夺同一个后继哈希地址导致出现堆积(或聚集)现象
非同义词发生冲突
第一次冲突为同义词引起的冲突,第二次开始的冲突是非同义词引起的冲突
同义词
具有相同函数值的多个关键字冲突的关键成为同义词
聚集
两个key的哈希地址不同,但出现争夺同一个后继地址后,这种现象称为聚集
将会造成不是同义词的元素也处于同一个探查序列中,从而增加了查找时间
出现聚集比较严重,可使用平方探查法
2、散列函数的构造方法
使用散列表要解决好两个问题:
-
构造好的散列函数
- 所选函数尽可能简单,以便提高转速度
- 所选函数对关键码计算出的地址,应在散列地址集中致均匀分布,以减少空间的浪费
- 尽可能使哈希地址出现在表中的任意位置的概率均等,从而减少冲突
-
制定一个好的解决冲突的方案
查找时,如果从散列函数计算出的地址中查找不到关键码,则应当依据解决冲突的规则,有规律地查询其他相关单元
-
构造散列函数考虑的因素
- 执行速度(即计算散列函数所需要的时间)
- 关键字的长度
- 散列表的大小(越大则产生冲突的可能性越小,但是浪费空间)
- 关键字的分布情况
- 查找频率
-
根据元素集合特性构造
- 要求一:n个数据原仅占n个地址,虽然散列查找时以空间换时间,但仍希望散列的地址空间尽量小
- 要求二:无论用什么方法存储,目的都是尽量均匀地存放元素,以避免冲突
-
常用的构造方法
- 直接定址法
- 数字分析法
- 平方取中法
- 折叠法
- 除留余数法
- 随机数法
-
直接定址法
前提是关键字基本连续
取关键字的本身或关键字的某个线性函数值作为哈希地址
Hsah ( key ) = a * key + b (a、b 为常数)
关键字和地址一一对应,不会产生冲突
只适用于关键字基本连续的情况
优点
:以关键码key的某个线性函数值为散列地址,不会产生冲突缺点
:要占用连续地址空间,空间效率低 -
除留余数法
Hash(key) = key mod p (p是一个不为0的整数)
关键
: 如何选取合适的P? p为质数(素数)时,冲突的可能性相对较少技巧
: 设表长为m,取p<=m 且为质数
3、处理冲突的方法
开放定址法(开地址法)
链地址法(拉链法)
再散列法(双散列函数法)
建立一个公共溢出区
第一次冲突为同义词引起的冲突,第二次开始的冲突是非同义词引起的冲突
冲突原因与三个因素有关
1、装填因子a(load factor):
- a = 存储的记录个数/哈希表的大小
- a为0.6~0.9时产生冲突的可能性比较小(
既兼顾减少冲突的发生,又兼顾提高存储空间的利用率
) - 如有元素600个,则表长为667~1000较为合适
2、哈希函数:
- 好的哈希函数计算出的哈希值会均匀分布在哈希表的整个地址区间,从而减少冲突
3、处理冲突的方法
好的处理冲突的方法可以减少产生二次冲突
- 开发定址法
- 线性探测法(优点:解决冲突简单 缺点:但是容易产生堆积问题)
- 平方探测法(优点:避免出现了堆积问题 缺点:不一定能探测出哈希表上的所有单元)
- 拉链法
1、开放定址法(开地址法)
基本思想
:有冲突时就去寻找下一个空的散列地址,只要散列表足够强大,空的散列地址总能找到,并将数据元素存入
例如:除留余数法 Hi = (Hash(key)+ d)mod m d为增量序列
常用方法:线性探测法 d为 1,2,…m-1 线性序列
二次探测法 d 为 1的平方,-1的平方,2的平方,-2的平方,…, q的平方 二次序列
伪随机探测法 d 为伪随机数序列
2、链地址法(拉链法)
基本思想
:相同散列地址的记录链成一单链表
m个散列地址就设m个单链表,然后用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构
哈希地址相同的元素放置于一个单链表,链表的头指针放置于对应的哈希地址处
链地址法建立散列表步骤
- 取数据元素的关键字key,计算其散列函数值(地址),若该地址对应的链表为空,则将该元素插入此链表;否则执行下一步解决冲突
- 根据选择的冲突处理方法,计算关键字key的下一个存储地址。若该地址对应的链表不为空,则利用链表的前插法或后插法将该元素插入此链表
链地址法的优点:
-
非同义词不会冲突(无堆积),无“聚集”现象,因此平均查找长度较短
-
链表上结点空间动态申请,更适合于表长不确定的情况
-
开放定址法为减少冲突要求装填因子a比较小,故当数据规模比较大时会浪费很多空间,
而拉链法中的装填因子a可取>=1,且元素较大时拉链法中增加的指针域可以忽略不计,因此节省空间
-
再用拉链法构造的哈希表中,删除结点的操作更加容易实现
拉链法的缺点
指针需要额外的空间,故当元素规模比较小时,开放定址法较为节省空间,若将节省的指针空间用来扩大哈希表的规模,可使装填因子变小,这又减少了开放定址法中的冲突,从而提高了平均查找速度
4、散列表查找
对于关键字集(19,14,23,1,68,20,84,27,55,11,10,79), n = 12
无序表查找ASL? 6.5
有序表折半查找ASL ? 3.+
那么,散列表上查找ASL?
5、散列表的查找效率分析
使用平均查找长度ASL来衡量查找算法,ASL取决于
-
散列函数
-
处理冲突的方法
-
散列表的装填因子a = 表中填入的记录数 / 哈希表的长度
a 越大,表中记录数越多,说明表装得越满,发生冲突的可能性越大,查找时比较次数就越多
6、散列表的删除
在采用开放地址法处理冲突的哈希表上执行删除操作时不能简单地将被删除元素的空间置为空,否则将截断在它之后填入哈希表的同义词元素的查找路径,这是因为在各种开放地地址法中,空地址单元都是查找失败的条件,因此只能在被删除元素上做删除标记,而不能真正地删除元素
而拉链表不同于开放地址法构造的哈希表,可以直接删除结点
7、结论
- 散列表技术具有很好的平均性能,优于一些传统的技术
- 链地址法优于开放地址法
- 除留余数法作散列函数优于其他类型函数
- 实际上,关键字的取值区间远大于哈希地址的变化区间
- 当一组数据的关键字与存储地址存在某种映射关系时,这组数据适合于哈希表存储
- 在一般情况下,假设哈希函数时均匀的,则可以证明不同的解决冲突方法得到的哈希表的平均查找长度不同。
- 哈希表的平均查找长度不是元素个数n的函数,而是装填因子a的函数,因此,在设计哈希表时可以选择合适的a以控制哈希表的平均查找长度