学习 严蔚敏讲数据结构笔记22

来源:互联网 发布:php获取上上个文件夹名 编辑:程序博客网 时间:2024/06/08 08:58

一、哈希表是什么?

从上面两节讨论的表示查找表的各种结构,有一个共同点:记录在表中的位置和它的关键字之间不存在一个确定的关系,因此,查找的过程为给定值依次和关键字集合中各个关键字进行比较,查找的效率取决于和给定值进行比较的关键字的个数。

用这类方法表示的查找表,其平均查找长度都不为零,不同表示方法的差别仅在于:和给定值进行比较的关键字的顺序不同。

对于频繁使用的查找表,希望ASL=0。只有一个办法:预先知道所查关键字在表中的位置,也就是说,记录在表中位置和其关键字之间存在一种确定的关系。

例如:

为每年招收的1000名新生建立一张查找表,其关键字xx000~xx999(前两位为年份)。则可以下标为000~999的顺序表表示之。由于关键字和记录在表中的序号相同,则不需要经过比较即可确定待查关键字。

但是,对于动态查找表而言,

1)  表长不确定;

2)  在设计查找表时,只知道关键字所属范围,而不知道确切的关键字。

因此,一般情况,需建立一个函数关系,以f(key)作为关键字为key的记录在表中的位置,通常称这个函数f(key)为哈希函数。

简单地说,哈希表是基于哈希函数建立的一种查找表。

例如:对于如下9个关键字

{Zhao,Qian,Sun,Li,Wu,Chen,Han,Ye,Dei}

13   8   9  6 11 1   4   12 2

f(key)=向下取整[(Ord(第一个字母)-Ord(‘A’)+1)/2]

从这个例子可见,

1)  哈希函数是一个映象,即:将关键字的结婚映射到某个地址结合上,它的设置很灵活,只要这个地址集合的大小不超出允许范围即可。

2)  由于哈希函数是一个压缩映象,因此,在一般情况下,很容易产生“冲突“现象,即:key1<>key2,而f(key1)=f(key2)并且,改进哈希函数只能减少冲突,而不能避免冲突。

因此,在设计哈希函数时,一方面要考虑选择一个“好“的哈希函数;另一方面要选择一种处理冲突的方法。

 

二、哈希函数的构造方法

对数字的关键字可有下列哈希函数的构造方法,若非数字关键字,则需先对其进行数字化处理。

1.      直接定址法

2.      数字分析法

3.      平方取中法

4.      折叠法

5.      除留余数法

6.      随机数法

 

1.      直接定址法

哈希函数为关键字的线性函数H(key)=key或者H(key)=a*key+b

仅限于:地址集合的大小=关键字集合的大小

2.      数字分析法

假设关键字集合中的每个关键字都是由s位数字组成(k1,k2,…,kn),分析关键字集中的全体,并从中提取分布均匀的若干位或它们的组合作为地址。

仅限于:能预先估计出全体关键字的每一位上各种数字出现的频度。

3.      平方取中

若关键字的每一位都有某些数字重复出现频度很高的现象,则先求关键字的平方值,以通过“平方“扩大差别,同时平方值的中间几位受到整个关键字中各位的影响。

4.      折叠法

若关键字的位数特别多,则可将其分割成几部分,憨厚取它们的叠加和为哈希地址。

可有:移位叠加和间界叠加两种处理方法。

5.      除留余数法

H(key)=key MOD p p<=m表长)

关键问题是:如何选取p

P应为不大于m的质数或是不含20以下的质因子

例如:当key=123918243321时,若取p=9,则使所有含质因子3的关键字均映射到地址036上,从而增加了“冲突“的可能性。

6.      随机数法

H(key)=Random(key)

 

实际造表时,采用何种构造哈希数的方法取决于建表的关键字集合的情况(包括关键字的范围和形态),总的原则是使产生冲突的可能性降到尽可能地小。

 

三、处理冲突的方法

处理冲突的实际含义:为产生冲突的地址寻找下一个哈希地址。

1.      开放定址法

2.      链地址法

 

1.开放定址法

为长生冲突的地址H(key)求得一个地址序列:

H0,H1,H2,…,Hs  1<=s<=m-1

其中:H0=H(key) H1=(H(key)+Di) MOD m  i=1,2,…s

增量di有三种取法

1)  线性探测在散列 di=c*I最简单的情况c=1

2)  平方探测散列 d1=12,-12,22,-22,….,

3)  随机探测在散列di是一组随机数列或者di=i*H(key)

注意:增量di应具有“完备性”

即:产生的Hi均不相同,且所产生的s(m-1)Hi值能覆盖哈希表中所有的地址。则要求:

平方探测时的表长m必为4j+3的质数;

随机探测时的mdi没有公因子。

 

2.链地址法

将所有哈希地址相同的记录都链接在同一个链表中。

 

四、哈希表的查找

查找过程和造表的过程一致,假设采用开放定址处理冲突,则查找过程为:

对于给定值K,计算哈希地址i=H(K)

r[i].key = NULLKEY则查找不成功

r[i].key = KEY则查找成功

否则求下一个地址Hi,直至

r[Hi].key = NULLKEY(查找不成功)r[Hi].key = K(查找成功)为止。

40_001

//开放定址哈希表的存储结构

int hashsize[] = {997,...};

typedef struct

{

         ElemType  *elem;

         int  count; //当前数据元素个数

         int  sizeindex; //hashsize[sizeindex]当前容量

}HashTable;

#define SUCCESS 1

#define UNSUCCESS 0

#define DUPLICATE -1

 

Status SearchHash(HashTable H, KeyType K,  int &p, int &c)

{

         //在开放定址哈希表H中查找关键字码为K的元素

         p  = Hash(K); //求得哈希地址

         while(H.elem[p].key  != NULLKEY && !EQ(K, elem[p].key))

                   collision(p,  ++c); //求得下一个探查地址p

         if(EQ(K,  H.elem[p].key))

                   return  SUCCESS; //查找成功,p返回待查数据元素位置

         else

                   return  UNSUCCESS; //查找不成功

}//SearchHash

决定哈希表查找的ASL的因素:

1)  选用的哈希函数

2)  选用处理冲突的方法

3)  选用哈希表饱和程度,装载因子a=n/m值的大小

一般情况下,可以认为选用的哈希函数是“均匀”的,在讨论ASL时,可以不考虑它的因素。

哈希表的ASL是处理冲突方法和装载因子的函数。

可以证明:查找成功时有下列结果: