目录
1.1哈希表的含义
Hash表的含义
1)Hash
表,又称散列表。在前面讨论的顺序、折半、分块查找和树表的查找中,其
ASL
的量级在
O(n)
~
O(log2n)
之间。
2)不论
ASL
在哪个量级,都与记录长度
n
有关。随着
n
的扩大,算法的效率会越来越低。
ASL
与
n
有
关是因为记录在存储器中的存放是随机的,或者说记录的key
与记录的存放地址无关,因而查找只
能建立在
key
的“比较”基础上。
3)理想的查找方法是:对给定的
key
,不经任何比较便能获取所需的记录,其查找的时间复杂度为常
数级
O(C)
。这就要求在建立记录表的时候,确定记录的key
与其存储地址之间的关系
f
,即使
key
与
记录的存放地址
H
相对应:
或者说,记录按key存放。
之后,当要查找key=k
的记录时,通过关系
f
就可得到相应记录的地址而获取记录,从而免去
了
key
的比较过程。这个关系f
就是所谓的
Hash
函数(或称散列函数、杂凑函数),记为
H(key)
。它
实际上是一个地址映象函数,其自变量为记录的key
,函数值为记录的存储地址(或称
Hash
地
址)。
以上通俗来说就是将一串数据按照一个函数(这里可以简单理解为加减乘除)规则计算地址,并按
照这个地址进行存储。
查找数据时,就把要查找的数据进行之前编码的函数计算从而获得数据的地址,然后直接
找到它。
举个例子:
张三 李四 王五 林朝夕
以上是我要存储的数据
假如计算地址的函数是提取每个数据的第一个字符
那我的数据地址就为
王 林 李 张 (顺序无关)
数据存储形式就为
所以,当我要查找 林朝夕 这个数据就可以通过函数计算得到 林 这个地址,从而直接找到林
朝夕这个数据。
那么这时候就有聪明的小伙伴会问,如果有同姓的名字或者完全相同的名字怎么办?
接下来我们就来解决这个问题
另外,不同的
key
可能得到同一个
Hash
地址,即当
key
l
≠key
2
时,可能有
H(key
1
)=H(key
2
)
,此时称
key
1
和
key
2
为
同义词
。
这种现象称为“
冲突
”或“碰撞”,因为一个数据单位只可存放一条记录。
一般,选取
Hash
函数只能做到使冲突尽可能少,却不能完全避免。这就要求在出现冲突之后,寻
求适当的方法来解决冲突记录的存放问题。
综上根据选取的
Hash
函数
H(key)
和处理冲突的方法,将一组记录
(R
1
R
2
……R
n
)
映象到记录的存储
空间,所得到的 记录表称为Hash表,如图:
1.2哈希函数(表)的构造方法
关于Hash表的讨论关键是两个问题,一是选取Hash函数的方法;二是确定解决冲突的方法。
选取(或构造)
Hash
函数的方法很多,原则是尽可能将记录均匀分布,以减少冲突现象的发生。
以下介绍两种常用的构造方法。
直接地址法
平方取中法
叠加法
除留余数法
随机函数法
直接地址法
此方法是取key的某个线性函数为Hash函数,即令:
H(key)=a
·
key+b
其中a、b为常数,此时称H(key)为直接Hash函数或自身函数。
这类哈希函数是一对一的映射,一般不会产生冲突。但是,它要求哈希地址空间的大小与关键字集
合的大小相同。例如,有一个从1~100岁的人口数字统计表,其中,年龄作为关键字(就是要存储
的数据),哈希函数取关键字自身(a = 1,b = 0),其哈希表如下表所示
地址 | 01 | 02 | ... | 25 | 26 | 27 | ... | 100 |
年龄 | 1 | 2 | ... | 25 | 26 | 27 | ... | ... |
人数 | 3000 | 2000 | ... | 1050 | ... | ... | ... | ... |
... |
除留余数法
又称质数除余法,设Hash表空间长度为m,选取一个不大于m的最大质数p,令:
H(key)=key%p
例如:m=8 16 32 64 128 256 512 1024
……
p=7 13 31 61 127 251 503 1019
……
举个例子:
设记录的key集合k={28,35,63,77,105
……
},
若选取p=21=3*7(包括质数因子7)
,有:
key:28 35 63 77 105
……
H(key)=key%21: 7 14 0 14 0
……
使得包含质数因子7的key都可能被映象到相同的单元,冲突现象严重。
若取p=l9(质数),同样对上面给定的key 集合k
,有:
key:28 35 63 77 105
H(key)=key%19: 9 16 6 1 10
H(key)的随机度就好多了。
1.3处理冲突的方法
以上是构造哈希函数避免冲突,但我们往往发现依然存在少量冲突,所以我们要寻求解决冲突的方法。
开放地址法
当发生冲突时,在H(key)的前后找一个空闲单元来存放冲突的记录,即在H(key)的基础上获取下一
地址:
H
i
=(H(key)+d
i
)%m
其中m为表长,%运算是保证H
i
落在[0,m-l]区间;d
i
为地址增量。d
i
的取法有多种:
(1)di=1,2,3,……(m-1)——称为线性探查法;
(2)di=12,-12,22,-22,……——称为二次探查法。
式(1)、(2)表示:第1次发生冲突时,地址增量d1取1或12;再冲突时,d2取2或-l2,……,依此类推。
设记录的key集合
k={23,34,14,38,46,16,68,15,07,31,26}
,记录数
n
=11。令装填因
子
α
=0.75, 取表长
m
=
n/α
=15。用“保留余数法”选取Hash函数(
p
=13):
H(key)=key%13
采用“线性探查法”解决冲突。依据以上条件,依次取k中各值构造的Hash表HT,如下图所示(表
HT初始为空)
229
k={23
,
34
,
14
,
38
,
46
,
16
,
68
,
15
,
07
,
31
,
26}
H(key)=key%13; H
i
=(H(key)+d
i
)%15; d
i
=1
,
2
,
3
,
……
(m-
1
H(68)=68%13=3(
冲突
),
取
H
1
=(3+1)%15=4(
空
),
故
68
存入
4
单元。
15
68
H(07)=7%13=7(
冲突
),
取
H
1
=(7+1)%15=8(
冲突
),
取
H
2
=(7+2)%15=9(
空
),
故
07
存入
9
单元。
07
31
26
若采用二次探测法:
d
i
=1
2
,
-1
2
,
2
2
,
-2
2
,
……
,表为:
其中,
H(07)=7%13=7(
冲突
)
, 取
H
1
=(7+ 1
2
)%15=8(
冲突
)
,取
H
2
=(7 -1
2
)%15=6(
空
)
,故
07
存
入
6 单元。
#define m 64 //设定表长m//
typedef struct
{ keytype key; //记录关键字
……
}Hretype;
Hretype HT[m]; //Hash表存储空间//
int Lhashsearch(Hretype HT[m],keytype k) //线性探查法解决冲突时的查找//
{
int j,d,i=0;
j=d=H(k); //求Hash地址并赋给j和d//
while((i<m)&&(HT[j].key!=NULL)&&(HT[j].key!=k))
{ i++; j=(d+i)%m; } //冲突时形成下一地址//
if(i==m) return(-1); //表溢出时返回-1//
else return(j);
} //HT[j].key==k,查找成功;HT[j].key==NULL,查找失败//
void LHinsert(Hretype HT[m], Hretype R) //记录R插入Hash表的算法//
{
int j=LHashsearch(HT,R.key); //查找R,确定其位置//
if((j= = -1)||(HT[j].key==R.key)) ERROR(); //表溢出或记录已存//
else HT[j]=R;
//插入HT[j]单元//
}
链地址法
发生冲突时,将各冲突记录链在一起,即同义词的记录存于同一链表。
设H(key)取值范围(值域)为[0,m-l],建立头指针向量HP[m],HP[i](0≤i≤m-l)初值为空。凡
H(key)=i的记录都
链入头指针为HP[i]的链表。
例10
设H(key)=key%13,其值域为[0,12], 建立指针向量 HP[l2]。 对例9中:
k={ 23,34,14,38,46,
16,68,15,07,31,26 }
依次取其中各值,用链地址法
解决冲突时的Hash表如
图
:
230
链地址法解决冲突的优点:无聚积现象;删除表中记录容易实现。而开放地址法的Hash表作删除
时,不能将记录所在单元置空,只能作删除标记。
typedef struct node //记录对应结点//
{
keytype key;
……
struct node *next;
}Renode;
Renode *LinkHsearch(Renode *HT[m],keytype k) //链地址法解决冲突时的查找//
{
Renode *p;
int d=H(k); //求Hash地址d//
p=HT[d]; //取链表头结点指针//
while(p&&(p->key!=k))
p=p->next; //冲突时取下一同义词结点//
return(p);
}
//查找成功时p->key==k,否则p=NULL//
当然还有 再哈希法 和 建立公共溢出区法
221