静态查找表 动态查找表 哈希表 (Hash)

45
静静静静静 静静静静静 静静静 (Hash)

description

第九章 查找. 静态查找表 动态查找表 哈希表 (Hash). 问题引入. 前面的查找方法是基于 比较 的 数组存储可以实现用下标立即取得 目标数据 现实问题中,经常遇到按给定的值进行快速查找(查询)的事例。 例如,使用文件名查找活动文件,程序语言的关键字查找。 按内容查找,不用比较,立即取得所查找记录。 需要考虑 记录存放位置和用以标识它的关键码 之间的对应关系,从而选择适当的数据结构,很方便地根据记录的关键码检索到对应记录的信息。. 9.3.1 哈希表 (Hash). Hash 表又称散列表。 - PowerPoint PPT Presentation

Transcript of 静态查找表 动态查找表 哈希表 (Hash)

Page 1: 静态查找表 动态查找表 哈希表  (Hash)

静态查找表 动态查找表 哈希表 (Hash)

Page 2: 静态查找表 动态查找表 哈希表  (Hash)

问题引入• 前面的查找方法是基于前面的查找方法是基于比较比较的的• 数组存储可以实现用下标立即取得数组存储可以实现用下标立即取得目标数据目标数据

现实问题中,经常遇到按给定的值进行快速查找(查现实问题中,经常遇到按给定的值进行快速查找(查询)的事例。 例如,使用文件名查找活动文件,程序语言询)的事例。 例如,使用文件名查找活动文件,程序语言的关键字查找。的关键字查找。

按内容查找,不用比较,立即取得所查找记录。按内容查找,不用比较,立即取得所查找记录。

需要考虑 记录存放位置和用以标识它的关键码 之间的需要考虑 记录存放位置和用以标识它的关键码 之间的对应关系,从而选择适当的数据结构,很方便地根据记录的对应关系,从而选择适当的数据结构,很方便地根据记录的关键码检索到对应记录的信息。关键码检索到对应记录的信息。

Page 3: 静态查找表 动态查找表 哈希表  (Hash)

9.3.1 哈希表 (Hash) Hash 表又称散列表。 Hash 函数 为记录存放位置和数据项 (关键码 )凑一个对应关系。

即:记录的存储位置 loc = h(key) 实现立即查找

Page 4: 静态查找表 动态查找表 哈希表  (Hash)

哈希 (Hash) 表如: hash 函数 h(key) = key mod 10, 记录集合为No. Name Class …

5 Zhang c112 Liu c213 Wang c110 Li c3…

则 Hash 表为 10 Li c3

12 Liu c2

4 Wang c15 Zhang c3

0

123

4567

loc

key

Page 5: 静态查找表 动态查找表 哈希表  (Hash)

C++ 的保留字

Page 6: 静态查找表 动态查找表 哈希表  (Hash)

9.3.2 哈希函数的构造方法

直接定址法 数字分析法 平方取中法 折叠法 除留余数法 随机数法

Page 7: 静态查找表 动态查找表 哈希表  (Hash)

构造哈希函数时的几点要求:构造哈希函数时的几点要求: 哈希函数的定义域必须包括需要存储的全部关哈希函数的定义域必须包括需要存储的全部关 键码,如果哈希表允许有 键码,如果哈希表允许有 m m 个地址时个地址时 , , 其其值域值域 必须在 必须在 0 0 到 到 mm--1 1 之间。之间。 哈希函数计算出来的地址应能均匀分布在整个哈希函数计算出来的地址应能均匀分布在整个 地址空间中:若 地址空间中:若 key key 是从关键码集合中随机是从关键码集合中随机抽抽 取的一个关键码,哈希函数应能以同等概率取的一个关键码,哈希函数应能以同等概率取取 0 0 到 到 mm--1 1 中的每一个值。中的每一个值。 哈希函数应是简单的,能在较短的时间内计算哈希函数应是简单的,能在较短的时间内计算 出结果。出结果。

哈希函数

Page 8: 静态查找表 动态查找表 哈希表  (Hash)

例如:有一个从 1 到 100 岁的人口数字统计表,其中,年龄作为关键字,哈希函数取关键字自身。

地址 01 02 ... 25 26 27 ... 100

年龄 1 2 ... 25 26 27 ... ...

人数 3000 2000 ... 1050 ... ... ... ...

...                

1、直接定址法

Page 9: 静态查找表 动态查找表 哈希表  (Hash)

2、数字分析法

有学生的生日数据如下:年 . 月 . 日75.10.0375.11.2376.03.0276.07.1275.04.2176.02.15...经分析 , 第一位,第二位,第三位重复的可能性大,取这三位造成冲突的机会增加,所以尽量不取前三位,取后三位比较好。

Page 10: 静态查找表 动态查找表 哈希表  (Hash)

3 .平方取中法取关键字平方后的中间几位为哈希函数地址。这是一种比较常用的哈希函数构造方法,但在选定哈希函数时不一定知道关键字的全部信息,取其中哪几位也不一定合适,而一个数平方后的中间几位数和数的每一位都相关,因此,可以使用随机分布的关键字得到哈希函数地址。

如图中,随机给出一些关键字,并取平方后的第2 到 4 位为函数地址。

关键字 (关键字)2 函数地址 0100 0010000 010 1100 1210000 210 1200 1440000 440 1160 1370400 370 2061 4310541 310

利用平方取中法得到哈希函数地址

Page 11: 静态查找表 动态查找表 哈希表  (Hash)

将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址,这方法称为折叠法折叠法。例如:每一种西文图书都有一个国际标准图书编号,它是一个 10位的十进制数字,若要以它作关键字建立一个哈希表,当馆藏书种类不到 10,000 时,可采用此法构造一个四位数的哈希函数。如果一本书的编号为 0-442-20586-4, 则:

5864 58644220 0224

+) 04 +) 04----------- -----------

10088 6092H(key)=0088 H(key)=6092

       (a) 移位叠加 (b) 间界叠加

Page 12: 静态查找表 动态查找表 哈希表  (Hash)

5. 除留余数法 设计哈希表中允许的地址数 , 取一个接近于所需大小 m 的质数 p, 利用以下公式把关键码转换成哈希地址。哈希函数为:

hash ( key ) = key % p 其中 , “%”是整数除法取余的运算。

例:有一个关键码 key = 962148 ,取质数 p= 23 。哈希函数 hash ( key ) = key % p 。则哈希地址为 hash ( 962148 ) = 962148 % 23 = 12 。

Page 13: 静态查找表 动态查找表 哈希表  (Hash)

6、随机数法

选择一个随机函数,取关键字的随机函数值为它的哈希地址,即

H(key)=random(key) ,

其中 random 为随机函数。通常用于关键字长度不等时采用此法。

Page 14: 静态查找表 动态查找表 哈希表  (Hash)

哈希 (Hash) 表 使用哈希方法进行搜索不必进行多次关键码的比较,搜索

速度比较快。 哈希函数是一个压缩映象函数。关键码取值范围比哈希表

地址集合大得多。因此很可能经过哈希函数的计算,把不同的关键码映射到同一个哈希地址上,这就产生了冲突 (Collision) 。

例:一个班的同学有人在同一天过生日的机会是多大?

Page 15: 静态查找表 动态查找表 哈希表  (Hash)

既然冲突很难避免。所以对于哈希方法,需要讨论以下两个重要问题:

对于给定的一个关键码集合,选择一个计算简单且地址分布比较均匀的哈希函数,避免或尽量减少冲突;

拟订解决冲突的方案。

两个努力方向

Page 16: 静态查找表 动态查找表 哈希表  (Hash)

解决冲突的方法又称为溢出处理技解决冲突的方法又称为溢出处理技术。术。

• 开放地址法开放地址法• 链地址法链地址法• 再哈希法再哈希法• 公共溢出区公共溢出区

9.3.3 9.3.3 冲突处理方法冲突处理方法

Page 17: 静态查找表 动态查找表 哈希表  (Hash)

开放地址法 若设哈希表中的编址为 0 到 m-1 ,当要加入一个项 R2时 , 用它的关键码 R2.key ,通过哈希函数 hash ( R2.key ) 的计算,得到它的存放地址号 j,但是在存放时发现这个位置已经被另一个 R1 占据了。这时发生了冲突,必须处理溢出。为此,必须把 R2 存放到表中“下一个”空位中。如果表未被装满,则在允许的范围内必定还有空位。

Page 18: 静态查找表 动态查找表 哈希表  (Hash)

(1) 线性探查法 (Linear Probing)

需要搜索或加入一个表项时,使用哈希函数计算号: H0 = hash ( key ) 一旦发生冲突,在表中顺次向后寻找“下一个”空 Hi 的

公式为: Hi = ( H0 + i ) % m , i =1, 2, …, m-1 亦可写成 Hi = ( Hi-1 +1 ) % m , i =1, 2, …, m-1

Page 19: 静态查找表 动态查找表 哈希表  (Hash)

线性探查法 例 假设给出一组表项,它们的关键码为 Burke, Ekers, Broad, Blum, Attlee, Alton, Hecht, Ederly 。采用的哈希函数是:取其第一个字母在字母表中的位置。

Hash (x) = ord (x) - ord (‘A’) //ord() 是求字符内码的函数 这样,可得 Hash (Burke) = 1 Hash (Ekers) = 4 Hash (Broad) = 1 Hash (Hecht) = 7 Hash (Attlee) = 0 Hash (Blum) = 1

Page 20: 静态查找表 动态查找表 哈希表  (Hash)

又设哈希表为 HT[26], m = 26 。采用线性探查法处理溢出,则上述关键码在哈希表中哈希位置如图所示。括号内的数字表示找到空位时的比较次数。

0 1 2 3 40 1 2 3 4

Attlee Burke Broad Ekers

Blum

Hecht

5 6 7 8 9 5 6 7 8 9

(1) (1) (2) (1)(1) (1) (2) (1)

(1)(1)

Page 21: 静态查找表 动态查找表 哈希表  (Hash)

又设哈希表为 HT[26], m = 26 。采用线性探查法处理溢出,则上述关键码在哈希表中哈希位置如图所示。括号内的数字表示找到空位时的比较次数。

0 1 2 3 40 1 2 3 4

Attlee Burke Broad Ekers

Blum

Hecht

5 6 7 8 9 5 6 7 8 9

(1) (1) (2) (1)(1) (1) (2) (1)

(1)(1)

Page 22: 静态查找表 动态查找表 哈希表  (Hash)

又设哈希表为 HT[26], m = 26 。采用线性探查法处理溢出,则上述关键码在哈希表中哈希位置如图所示。括号内的数字表示找到空位时的比较次数。

0 1 2 3 40 1 2 3 4

Attlee Burke Broad Ekers

Blum

Hecht

5 6 7 8 9 5 6 7 8 9

(1) (1) (2) (1)(1) (1) (2) (1)

(1)(1)

Page 23: 静态查找表 动态查找表 哈希表  (Hash)

又设哈希表为 HT[26], m = 26 。采用线性探查法处理溢出,则上述关键码在哈希表中哈希位置如图所示。括号内的数字表示找到空位时的比较次数。

0 1 2 3 40 1 2 3 4

Attlee Burke Broad EkersBlum

Hecht

5 6 7 8 9 5 6 7 8 9

(1) (1) (2) (3) (1)(1) (1) (2) (3) (1)

(1)(1)

Page 24: 静态查找表 动态查找表 哈希表  (Hash)

当发生冲突时 , 探查下一个位置。当循环 m-1 次后就会回到开始探查时的位置 , 说明待查关键码不在表内 , 而且表已满 , 不能再插入新关键码。

有聚集现象

Page 25: 静态查找表 动态查找表 哈希表  (Hash)

(2) 二次探查法 (quadratic probing) 为改善聚集现象,减少为完成搜索所需的平均探查次数,可使用二次探查法。

通过某一个哈希函数对表项的关键码 x 进行计算,得到地址号,它是一个非负整数。

H0 = hash(x) 二次探查法在表中寻找“下一个”空位的公式为: Hi = (H0 i 2 ) % m , i = 1, 2, …, (m-1)/2 式中的 m 是表的大小,它应是一个值为 4k+3 的质数,其中 k是一个整数。这样的质数如 3, 7, 11, 19, 23, 31, 43, 59, 127, 251, 503, 1019, …。

Page 26: 静态查找表 动态查找表 哈希表  (Hash)

(2) 二次探查法 (quadratic probing)

快速计算方法 p = 1 Hi = (Hi-1 p ) % m ,p = p+2, i = 1, 2, …, (m-1)/2

Page 27: 静态查找表 动态查找表 哈希表  (Hash)

二次探查法 例

示例:给出一组关键码 示例:给出一组关键码 { Burke, Ekers, Broad, Blu{ Burke, Ekers, Broad, Blum, Attlee, Alton, Hecht, Ederly }m, Attlee, Alton, Hecht, Ederly } 。。

散列函数为:散列函数为: HashHash ( (xx)) =(=( ord ord ((xx)) -- ord ord ('('AA')') ))%% 2323

用它计算可得用它计算可得 Hash Hash (Burke) = 1 (Burke) = 1 HashHash (Ekers) = 4 (Ekers) = 4 HashHash (Broad) = 1 (Broad) = 1 HashHash (Blum) = 1 (Blum) = 1 HashHash (Attlee) = 0 (Attlee) = 0 HashHash (Hecht) = 7 (Hecht) = 7 HashHash (Alton) = 0 (Alton) = 0 HashHash (Ederly) = 4 (Ederly) = 4

Page 28: 静态查找表 动态查找表 哈希表  (Hash)

若设表的长度为 TableSize = 23 ,则利用二次探查法所得到的哈希结果如图所示。

0 1 2 3 4 5

Blum Burke Broad Ekers Ederly

Hecht

6 7 8 9 10 11

Alton Attlee

(3) (1) (2) (1) (2)

(1)

17 18 19 20 21 22

(5) (3)

利用二次探查法处理溢出利用二次探查法处理溢出

Page 29: 静态查找表 动态查找表 哈希表  (Hash)

练习, h(key) = key mod 11, 现在哈希表如下 :

新记录关键字为 38 , 其位置应为 38 mod 11=5. 有冲突!应插入到哪里 ?

60 17 290 1 2 3 4 5 6 7 8 9 10

60 17 29 380 1 2 3 4 5 6 7 8 9 10

线性探测 .

平方探测 .

3860 17 290 1 2 3 4 5 6 7 8 9 10

38

Page 30: 静态查找表 动态查找表 哈希表  (Hash)

链地址法 首先对关键码集合用某一个哈希函数计算它们的存放位置。

若设哈希表地址空间的所有位置是从 0到 m-1 ,则关键码集合中的所有关键码被划分为 m 个子集,具有相同地址的关键码归于同一子集。称同一子集中的关键码互为同义词。

通常同义词表项通过一个单链表链接起来,称之为同义词子表。各链表的表头结点组成一个向量。

Page 31: 静态查找表 动态查找表 哈希表  (Hash)

采用链地址法处理溢出,关键码为 Burke, Ekers, Broad, Blum, Attlee, Alton, Hecht, Ederly 。哈希函数与前面相同,则哈希表为:

Page 32: 静态查找表 动态查找表 哈希表  (Hash)

通常,每个同义词子表都很短,设有 n 个关键码通过某一个哈希函数,存放到哈希表中的 m 个桶中。那么每一个桶中的同义词子表的平均长度为 n / m 。这样,以搜索平均长度为 n / m 的同义词子表代替了搜索长度为 n 的顺序表,搜索速度快得多。

Page 33: 静态查找表 动态查找表 哈希表  (Hash)

再哈希法

公共溢出区

Page 34: 静态查找表 动态查找表 哈希表  (Hash)

其他问题

在利用哈希表进行各种处理之前,必须首先将哈希表中原有的内容清掉。只需将表中所有表项的 info 域置为 Empty 即可。

哈希表存放的表项不应有重复的关键码。在插入新表项时,如果发现表中已经有关键码相同的表项,则不再插入。

一般不能真正删除表中已有表项。删除表项会影响其它表项的搜索。若把关键码为 Broad的表项真正删除,把它所在位置的 info 域置为 Empty ,以后在搜索关键码为 Blum和 Alton 的表项时就查不下去,从而会错误地判断表中没有关键码为 Blum 和 Alton 的表项。

Page 35: 静态查找表 动态查找表 哈希表  (Hash)

若想删除一个表项,只能给它做一个删除标记 deleted,进行逻辑删除,不能把它真正删去。

逻辑删除的副作用是:在执行多次删除后,表面上看起来哈希表很满,实际上有许多位置没有利用。

Page 36: 静态查找表 动态查找表 哈希表  (Hash)

哈希表分析

哈希表是一种直接计算记录存放地址的方法,它在关键码与存储位置之间直接建立了映象。

当选择的哈希函数能够得到均匀的地址分布时,在搜索过程中可以不做多次探查。

Page 37: 静态查找表 动态查找表 哈希表  (Hash)

若设 是哈希表的装填因子:

用地址分布均匀的哈希函数 Hash( ) 计算地址。 Sn 是搜索一个随机选择的关键码 xi (1 i n) 所需的关键码比较次数的期望值

Un 是在长度为 m 的哈希表中 n 个地址已有记录的情况下,装入第 n+1 项所需执行的关键码比较次数期望值。

前者是搜索成功时的平均搜索长度,后者是搜索不成功时的平均搜索长度。

m

n

表的最大记录数表已有的记录数

算法分析

Page 38: 静态查找表 动态查找表 哈希表  (Hash)

当装填因子 较高时,选择的哈希函数不同,哈希表的搜索性能差别很大。

对哈希表技术进行的实验评估表明,它具有很好的平均性能,优于一些传统的技术,如平衡树。

但哈希表在最坏情况下性能很不好。如果对一 个有 n 个关键码的哈希表执行一次搜索或插入操作,最坏情况下需要 O(n) 的时间。

Page 39: 静态查找表 动态查找表 哈希表  (Hash)

用不同的方法处理冲突时哈希表的平均搜索长度如表所示。

处 理 冲 突 平 均 搜 索 长 度 A S L 的 方 法 搜 索 成 功 S n 搜 索 不 成 功 ( 登 入 新 记 录 ) U n

开 地

线 性 探 查 法

)(

址 法

伪 随 机 探 查 法 二 次 探 查 法

1log

1e

链 地 址 法

e

各种方法处理冲突的平均搜索长度

Page 40: 静态查找表 动态查找表 哈希表  (Hash)

哈希表的装填因子 表明了表中的装满程度。越大,说明表越满,再插入新元素时发生冲突的可能性就越大。

哈希表的搜索性能,即平均搜索长度依赖于哈希表的装填因子,不直接依赖于 n 或 m 。

不论表的长度有多大,我们总能选择一个合适的装填因子,以把平均搜索长度限制在一定范围内。

复杂度 O(1)

Page 41: 静态查找表 动态查找表 哈希表  (Hash)

理论分析结果

Page 42: 静态查找表 动态查找表 哈希表  (Hash)

实际测试结果

Page 43: 静态查找表 动态查找表 哈希表  (Hash)

完美的哈希函数

Page 44: 静态查找表 动态查找表 哈希表  (Hash)

总结 哈希表 查找

Page 45: 静态查找表 动态查找表 哈希表  (Hash)

作业 已知一关键字序列为 {87,25,310,08,132,68,95,

187,123,70,63,47} 。设哈希函数为 H(key) = key %13 ,采用链地址法处理冲突。画出最后存储的哈希表,计算该表查找成功时的平均查找长度。