散列表常见散列函数及处理冲突的方法
Posted 薛定谔的猫ovo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了散列表常见散列函数及处理冲突的方法相关的知识,希望对你有一定的参考价值。
散列表的概念
散列法
散列法又称哈希法或杂凑法,它在元素的存储位置与元素关键码之间建立一个确定的对应函数关系Hash(),使得每个关键码与结构中的一个唯一的存储位置相对应:
A
d
d
r
e
s
s
=
H
a
s
h
(
k
e
y
)
Address = Hash(key)
Address=Hash(key)
在插入时,依此函数计算存储位置并按此位置存放。
在查找时,对元素的关键码进行同样的函数计算,把求得的函数值当作元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则查找成功。这种方法就称作散列法。
散列函数
在散列法中使用的转换函数叫做散列函数。
散列表
按照散列法构造出来的表或结构就叫做散列表。
散列表是根据关键字直接进行访问的数据结构,也就是说,散列表建立了关键字和存储位置之间的一种直接映射关系。
常见的散列函数
<1> 直接定址法
对关键码做一个线性计算,把计算结果当作散列地址。
H
(
k
e
y
)
=
a
×
k
e
y
+
b
H(key) = a × key + b
H(key)=a×key+b
其中,
a
a
a和
b
b
b都是常数。
这种方式计算简单,且不会产生冲突。它适合关键字的分布基本连续的情况;若关键分布不连续,空位较多,则会造成存储空间的浪费。
<2> 除留余数法
设散列表的表长为
m
m
m,取一个不大于
m
m
m但最接近或等于
m
m
m的质数
p
p
p,利用以下公式将关键字转换成散列地址:
H
(
k
e
y
)
=
k
e
y
%
p
H(key) = key\\ \\%\\ p
H(key)=key % p
其中,
%
\\%
%是整数除法取余的运算。
除留余数法的关键是选好
p
p
p,使得每个关键字通过该函数转换后等概率地映射到散列空间上地任一地址,从而尽可能减少冲突的可能性。
<3> 数字分析法
设关键字是r进制数,而r个数码在各位上出现的频率不一定相同,可能在某些位上分布均匀一些,每种数码出现的机会均等;而在某些位上分布不均匀,只有某几种数码经常出现,此时应选取数码分布较均匀的若干位作为散列地址。
这种方法适合于已知的关键字集合,若更换了关键字,则需要重新构造新的散列函数。
<4> 平方取中法
这种方法取关键字的平方值的中间几位作为散列地址。
这种方法得到的散列地址与关键字的每位都有关系,因此使得散列地址分布比较均匀,适用于关键字的每位取值不够均匀或均小于散列地址所需的位数。
<5> 折叠法
将关键字分割成位数相同的几部分(最后一部分的位数可以短一些),然后取这及部分的叠加和作为散列地址,这种方法称为折叠法。
当关键字位数很多,而且关键字中的每位上数字分布大致均匀时,可以采用折叠法得到散列地址。
处理冲突的方法
可以注意到,任何设计出来的散列函数都不可能绝对地避免冲突。为此,必须考虑在发生冲突时应该如何处理,即为产生冲突的关键字寻找下一个“空”的Hash地址。
处理冲突的开放地址法
所谓开放地址法,是指可存放新元素的空闲位置既向它的同义词元素开放,又向它的非同义词元素开放。就是说,所有元素都放在一个散列表的基本空间之内。这里的同义词指的是那些散列地址相同的不同关键码。
其数学递归公式是:
H
i
=
(
H
(
k
e
y
)
+
d
i
)
%
m
H_i = (\\ H(key) + d_i\\ )\\ \\% \\ m
Hi=( H(key)+di ) % m
其中
H
i
H_i
Hi 表示发生冲突后第
i
i
i 次探测的散列地址;
i
=
0
,
1
,
2
,
.
.
.
,
k
(
k
≤
m
−
1
)
i=0,1,2,...,k(k≤m-1)
i=0,1,2,...,k(k≤m−1);
m
m
m表示散列表表长;
d
i
d_i
di为增量序列
确定某一增量序列后,对应的处理方法就是确定的。通常由以下4中取法:
<1> 线性探测法
当 d i = 0 , 1 , 2 , . . . , m − 1 d_i = 0,1,2,...,m-1 di=0,1,2,...,m−1时,称为线性探测法。
这种方法的特点:冲突发生时,顺序查看表中下一个单元(探测到表尾地址m-1时,下一个探测地址是表首地址0),直到找出一个空闲单元(当表未填满时一定能找到一个空闲单元)或查遍全表。
缺点:线性探测法可能使第 i i i个散列地址的同义词存入第 i + 1 i+1 i+1个散列地址,这样本应存入第 i + 1 i+1 i+1个散列地址的元素就争夺第 i + 2 i+2 i+2个散列地址的元素的地址……从而造成大量元素在相邻的散列地址上“聚集”起来,大大降低了查找效率。
例如,关键码为
37
,
25
,
14
,
36
,
49
,
68
,
57
,
11
\\37,25,14,36,49,68,57,11\\
37,25,14,36,49,68,57,11,散列函数为
H
a
s
h
(
k
e
y
)
=
k
e
y
%
11
Hash(key) = key \\% 11
Hash(key)=key%11,则各关键码计算出的地址为:
H
a
s
h
(
37
)
=
4
Hash(37)=4
Hash(37)=4,
H
a
s
h
(
25
)
=
3
Hash(25)=3
Hash(25)=3,
H
a
s
h
(
14
)
=
3
Hash(14)=3
Hash(14)=3,
H
a
s
h
(
36
)
=
3
Hash(36)=3
Hash(36)=3,
H
a
s
h
(
49
)
=
5
Hash(49)=5
Hash(49)=5,
H
a
s
h
(
68
)
=
2
Hash(68)=2
Hash(68)=2,
H
a
s
h
(
57
)
=
2
Hash(57)=2
Hash(57)=2,
H
a
s
h
(
11
)
=
0
Hash(11)=0
Hash(11)=0
得到的散列结果如图所示:
散列地址 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
关键字 | 11 | 68 | 25 | 37 | 14 | 36 | 49 | 57 | ||||
比较次数 | 1 | 1 | 1 | 1 | 3 | 4 | 3 | 7 |
那么 A S L 成 功 = 1 8 × ( 1 + 1 + 1 + 1 + 3 + 4 + 3 + 7 ) = 21 8 ASL_成功 = \\frac18 × (1+1+1+1+3+4+3+7) = \\frac218 ASL成功=81×(1+1+1+1+3+4+3+7)=821
<2> 平方探测法
当 d i = 0 2 , 1 2 , − 1 2 , 2 2 , − 2 2 , . . . , k 2 , − k 2 d_i = 0^2, 1^2,-1^2,2^2,-2^2,...,k^2,-k^2 di=02,12,−12,22,−22,...,k2,−k2时,称为平方探测法,其中 k ≤ m / 2 k≤m/2 k≤m/2,散列表长度必须是一个可以表示成 4 k + 3 4k+3 4k+3的素数,又称为二次探测法。
平方探测法是一种较好的处理冲突的方法,可以避免出现“堆积”问题,它的缺点是不能探测到散列表上的所有单元,但至少能探测到一半单元。
例如,关键码为
37
,
25
,
14
,
36
,
49
,
68
,
57
,
11
\\37,25,14,36,49,68,57,11\\
37,25,14,36,49,68,57,11,散列函数为
H
a
s
h
(
k
e
y
)
=
k
e
y
%
19
Hash(key) = key \\% 19
Hash(key)=key%19,则各关键码计算出的地址为: 以上是关于散列表常见散列函数及处理冲突的方法的主要内容,如果未能解决你的问题,请参考以下文章
H
a
s
h
(
37
)
=
18
Hash(37)=18
Hash(37)=18,
H
a
s
h
(
25
)
=
6
Hash(25)=6
Hash(25)=6,
H
a
s
h
(
14
)
=
14
Hash(14)=14
Hash(14)=14,
H
a
s
h
(
36
)
=
17
Hash(36)=17
Hash(36)=17,
H
a
s
h
(
49
)
=
11
Hash(49)=11
Hash(49)=11,
H
a
s
h
(
68
)
=
11