散列hash
散列
散列方法是一种计算式查找方法,又叫做hash(哈希)法或关键字地址计算。其基本思想是:
首先在记录的关键字
key
和记录的存储位置p
之间建立一个对应关系H
,使得p=H(key)
,H
称为hash函数,p
称为散列地址。当创建hash表时,把关键字为key
的记录直接存入地址为H(key)
地址单元中;以后当查找关键字为key
的记录时,再利用hash函数p=Hash(key)
直接计算出该记录的散列地址p
,从而达到直接存取记录的目的。因此,散列方法的核心就是由hash函数决定关键字值与散列地址之间的对应关系,通过这种关系来组织存储并进行查找等操作——《C数据结构与算法》王曙燕
哈希冲突
例如,记录序列的关键字值分别为
and,cell,do,flag,hot,ill,little,sit,yellow,zero
若hash函数H(key)
取值为关键字的第一个字母在字母表中的字典顺序,那么关键字会依次分布在散列地址中。但如果关键字表改为
and,apple,art,cell,do,flag,hot,ill,little,sit,sad,sorry,yellow,zero
就会发生and,apple,art和sit,sad,sorry这两组关键字会发生“地址冲突”——首字母相同,通过hash函数计算出的散列地址p
必然相同,即地址冲突(哈希冲突)。
在实际应用中,很少存在不产生冲突的哈希函数,因此在采用散列方法进行查找时必须解决的两个问题是:
- 如何恰当地构造hash函数,使得节点“均匀分布”,尽量少的产生冲突
- 一旦发生冲突,如何处理冲突
hash函数的构造方法
构造原则:
简单:函数本身尽量简单,便于计算
均匀:hash函数值必须在散列地址范围内,且分布均匀,地址冲突尽量少
1.除留余数法
假设表长为m,p为小于等于表长m的最大素数,则hash函数为H(key)=key%p
eg.关键字序列22,41,53,46,29,14,1,60,散列地址为0~10的范围,hash函数为
H(k)=k%11
;
2. 数字分析法
假设关键字集合中的每个关键字都是由s位数字组成,如果可以预先估计出全体关键字的每一位上各位数字出现的频度时,分析关键字集中的庪,从中提取分布均匀的若干位或它们的组合作为hash地址。
eg.图中的关键字位8位的十进制整数,经过分析,各个关键字中第4~6位中的取值比较均匀,则hash函数位
H(key)=d4d5d6
3.平方取中法
首先通过求关键字的平方值扩大相近数的差别,然后根据表中长度取中间的几位数作为hash函数值。又因为一个乘积的中间几位数和乘数的每一位都相关,所以由此产生的散列地址比较均匀。
eg.存在与一组补0十进制关键字(0100,0110,1010,1001,0111)平方后得:
(0010000,0012100,1020100,1002001,0012321)
若取表长为1000,则可取中间得三位数作为散列地址集:
(100,121,201,020,123)
诸多方法不一一列举了,还有分段叠加法,基数转换法等
处理冲突的方法
好的hash函数可以减少冲突,但实际上冲突是不可避免的,实际上处理冲突的实际含义就是为产生冲突的地址寻找下一个散列地址。
1.开放地址法
也称为再散列法。基本思想是
当关键字key的初始散列地址
H(key)=h0
出现冲突时,以h0
为基础寻找下一个地址h1
,如果h1
仍然冲突,再以h0
为基础,产生另一个散列地址h2
……直至找出一个不冲突的散列地址hi
,将相应元素存入其中,该类方法有一个通用的再散列函数形式:hi=(H(key)+di)%m,i=1,2,……,m
,其中H(key)为哈希函数,h0 =H(key)
,m
为表长,di
为增量序列。
增量序列的取值方式不同,对应有不同的再散列方式。
a.线性探测再散列
di=c*i,c=1
是最简单的情况
该方法的特点是:
冲突发生时,顺序查看表中的下一个单元,直至找到一个空单元或者查遍全表。要指出的是,由于使用%运算,因此整个表成为一个首尾连接的循环表。
b.二次探测再散列
di=12,-12,22,-22,……,k2,-k2(k<=m/2)
该方法的特点是:
冲突发生时,会相间地左右跳跃式探测表中的单元,较为灵活,不易产生聚集,但缺点是不能探查到整个散列地址空间
c.随机探测再散列
di=伪随机数
这种方法需要建立一个随机数发生器,并给定一个随机数作为起始点。
线性探测散列表模拟
2.链地址法
链地址法解决冲突的基本思想是把所有具有地址冲突的关键字链在同一个单链表中
若哈希表的长度为m,则可将哈希表定义为一个有m个头指针组成的指针数组。散列地址为i记录,均插入到以指针数组第i个单元为头指针的单链表中。
eg.关键字集合为{19,1,23,14,55,68,11,82,36},设哈希函数为
H(key)=key%7
,采用链地址法处理哈希冲突
1.首先计算各关键字的散列地址
2.其次,根据每个关键字确定的散列地址进行存放,如果地址有冲突,将冲突的记录链在同一个单链表中。
链地址法散列表模拟
结语
最后,不管用什么样的构造哈希表,只要数据是源源不断插入结构中,那么冲突的可能性会越来越大。
一般来说,哈希表的长度会在表中元素数目占据容量的70%时,会进行扩张,已扩大容量,此时会对表中元素进行重新映射。