赞
踩
散列表是根据关键码值 ( K e y (Key (Key v a l u e ) value) value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
其他术语:
等概率、均匀
地分布在整个地址空间中,从而减少冲突的发生。直接取关键字的某个线性函数值为散列地址,散列函数为:
H
(
k
e
y
)
=
k
e
y
H(key)=key
H(key)=key或
H
(
k
e
y
)
=
a
×
k
e
y
+
b
H(key)=a×key+b
H(key)=a×key+b
特点:
这是一种最简单、最常用的方法,假定散列表表长为
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,使得每个关键字通过该函数转换后等概率地映射到散列空间上的任意一个的地址,从而尽可能减少冲突的可能性。
如果关键字由多位字符或者数字组成,就可以考虑抽取其中的 2 2 2 位或者多位作为该关键字对应的哈希地址,在取法上尽量选择变化较多的位,避免冲突发生。
特点:
举例:
即取关键字平方的中间位数作为散列地址。
特点:
举例:
假设关键字是
4321
4321
4321,那么它的平方就是
18671041
18671041
18671041,抽取中间的
3
3
3 位就可以是
671
671
671,也可以是
710
710
710,用做散列地址。
选择一个随机数,取关键字的随机值作为它的散列地址,散列函数为
H
(
k
e
y
)
=
r
a
n
d
o
m
(
k
e
y
)
H(key)=random(key)
H(key)=random(key)
特点:
折叠法是将关键字从左到右分割成位数相等的几部分(注意最后一部分位数不够时可以短些),然后将这几部分叠加求和,并按散列表表长,取后几位作为散列地址。
举例:
假设关键字是 9876543210 9876543210 9876543210,散列表表长为三位,则我们可以将它分为四组 987 ∣ 654 ∣ 321 ∣ 0 987|654|321|0 987∣654∣321∣0,然后将它们叠加求和 987 + 654 + 321 + 0 = 1962 987+654+321+0=1962 987+654+321+0=1962,再取后 3 3 3 位得到散列地址即为 962 962 962。
有时可能这还不能够保证分布均匀,那么也可以尝试从一端到另一端来回折叠后对齐相加,比如讲 987 987 987 和 321 321 321 反转,再与 654 654 654和 0 0 0相加,变成 789 + 654 + 123 + 0 = 1566 789+654+123+0=1566 789+654+123+0=1566,此时散列地址为 566 566 566。
特点:
将可存放新表项的空闲地址既向它的同义词开放,又向它的非同义词开放。数学递推公式为:
H
i
=
(
H
(
k
e
y
)
+
d
i
)
%
m
H_i=(H(key)+d_i)\%m
Hi=(H(key)+di)%m
其中,
m
m
m散列表表长,
d
i
d_i
di表示增量序列。
当 d i = 0 , 1 , 2 , . . . , m − 1 d_i=0,1,2,...,m-1 di=0,1,2,...,m−1时,成为线性探测法。
冲突发生时,顺序查看表中下一个单元(探测到表尾地址 m − 1 m-1 m−1时,下一个探测地址是表首地址 0 0 0),直到找出一个空闲单元(当表未填满时一定能够找到一个空闲单元)或查遍全表。
特点:
当 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,散列表长度 m m m必须是一个可以表示成 4 k + 3 4k+3 4k+3的素数,又称为二次探测法。
特点:
增量 d i di di为伪随机数时,称为伪随机序列法。
当
d
i
=
H
a
s
h
2
(
k
e
y
)
d_i=Hash_2(key)
di=Hash2(key)时,称为双散列法。需要使用两个散列函数,当通过第一个散列函数
H
(
k
e
y
)
H(key)
H(key)得到的地址发生冲突时,则利用第二个散列函数
H
a
s
h
2
(
k
e
y
)
Hash_2(key)
Hash2(key)计算该关键字的地址增量。其具体函数形式如下:
H
i
=
(
H
(
k
e
y
)
+
i
×
H
a
s
h
2
(
k
e
y
)
)
%
m
H_i=(H(key)+i×Hash_2(key))\%m
Hi=(H(key)+i×Hash2(key))%m
初始探测位置
H
0
=
H
(
k
e
y
)
%
m
H_0=H(key)\%m
H0=H(key)%m,
i
i
i是冲突次数,初始为
0
0
0。在双散列法中,最多经过
m
−
1
m-1
m−1次探测就会遍历表中所有位置,回到
H
0
H_0
H0位置。
举例:
在长度为
11
11
11 的哈希表中已填写好
17
17
17、
60
60
60和
29
29
29 这
3
3
3 个数据,其中采用的哈希函数为:
H
(
k
e
y
)
=
k
e
y
%
11
H(key)=key \%11
H(key)=key%11,
现有第
4
4
4 个数据
38
38
38 ,当通过哈希函数求得的哈希地址为
5
5
5,与
60
60
60 冲突,则分别采用以上前
3
3
3种方式求得插入位置如下图所示:
对于不同的关键字可能会通过散列函数映射到同一地址,为了避免非同义词发生冲突,可以把所有的同义词存储到一个线性链表中,这个线性链表由其散列地址唯一标识。
举例:
假设关键字序列为
{
19
,
14
,
23
,
01
,
68
,
20
,
84
,
27
,
55
,
11
,
10
,
79
}
\{19,14,23,01,68,20,84,27,55,11,10,79\}
{19,14,23,01,68,20,84,27,55,11,10,79},散列函数为
H
(
k
e
y
)
=
k
e
y
%
13
H(key)=key\%13
H(key)=key%13,用拉链法处理,建立的表如下图所示:
散列表的查找过程与构造散列表的过程基本一致。对于一个给定的关键字 k e y key key,根据散列函数可以计算出其散列地址,执行步骤如下:
初始化: A d d r = H a s h ( k e y ) Addr=Hash(key) Addr=Hash(key)
举例:
下图为按照散列函数
H
(
k
e
y
)
=
k
e
y
%
13
H(key)=key\%13
H(key)=key%13和线性探测处理冲突构造所得的散列表:
(1)查找
84
84
84:首先求得散列地址
H
(
84
)
=
6
H(84)=6
H(84)=6,因为
L
(
6
)
L(6)
L(6)不为空且
L
(
6
)
!
=
84
L(6)!=84
L(6)!=84,则找到第一次冲突处理后的地址
H
1
=
(
6
+
1
)
%
16
=
7
H_1=(6+1)\%16=7
H1=(6+1)%16=7,而
L
(
7
)
L(7)
L(7)不为空且
L
(
7
)
!
=
84
L(7)!=84
L(7)!=84,则找到第二次冲突处理后的地址
H
2
=
(
6
+
2
)
%
16
=
8
H_2=(6+2)\%16=8
H2=(6+2)%16=8,而
L
(
8
)
L(8)
L(8)不为空且
L
(
8
)
=
84
L(8)=84
L(8)=84,查找成功,返回在表中的序号
8
8
8。
(2)查找
38
38
38:首先求得散列地址
H
(
38
)
=
12
H(38)=12
H(38)=12,因为
L
(
12
)
L(12)
L(12)不为空且
L
(
12
)
!
=
38
L(12)!=38
L(12)!=38,则找到第一次冲突处理后的地址
H
1
=
(
12
+
1
)
%
16
=
13
H_1=(12+1)\%16=13
H1=(12+1)%16=13,而
L
(
13
)
L(13)
L(13)为空,所以不存在关键字为18的记录。
查找各关键字的比较次数如下图:
平均查找长度$
A
S
L
=
(
1
∗
6
+
2
+
3
∗
3
+
4
+
9
)
/
12
=
2.5
ASL=(1*6+2+3*3+4+9)/12=2.5
ASL=(1∗6+2+3∗3+4+9)/12=2.5
对同一组关键字,设定相同的散列函数,则不同的处理冲突的方法得到的散列表不同,它们的平均查找长度也不同。
从散列表的查找过程可见:
(1)虽然散列表在关键字与记录的存储位置之间建立了直接映像,但由于“冲突”的产生,使得散列表的查找过程仍然是一个给定值和关键字进行比较的过程。因此,仍需要以平均查找长度作为衡量散列表的查找效率的度量。
(2)散列表的查找效率取决于三个因素:散列函数、处理冲突的方法和装填因子。
散列表的平均查找长度依赖于散列表的装填因子 α \alpha α,而不直接依赖于 n n n或 m m m。直观地看, α \alpha α越大,表示装填的记录越“满”,发生冲突的可能性越大,反之发生冲突的可能性越小。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。