260 likes | 444 Views
数据结构. (第二十一讲). 绍兴文理学院. 计算机系计算机应用教研室. 第 7 章 查 找 (4). 一、教学目的: 明确哈希表的有关概念;掌握哈希函数的构造方法;掌握常用的处理冲突的方法;掌握哈希查找平均查找长度的计算;初步掌握用开放定址法和链地址法解决冲突的算法;了解几个不同的处理冲突方法下的平均查找长度的公式;算法设计训练。. 二、教学重点: 哈希表的有关概念;哈希函数的构造方法;常用的处理冲突的方法;用开放定址法和链地址法解决冲突的算法;哈希查找平均查找长度的计算;算法设计训练。. 三、教学难点: 哈希表的构造及解决冲突的算法;算法设计。 四、教学过程:.
E N D
数据结构 (第二十一讲) 绍兴文理学院 计算机系计算机应用教研室
第7章 查 找(4) 一、教学目的:明确哈希表的有关概念;掌握哈希函数的构造方法;掌握常用的处理冲突的方法;掌握哈希查找平均查找长度的计算;初步掌握用开放定址法和链地址法解决冲突的算法;了解几个不同的处理冲突方法下的平均查找长度的公式;算法设计训练。 二、教学重点:哈希表的有关概念;哈希函数的构造方法;常用的处理冲突的方法;用开放定址法和链地址法解决冲突的算法;哈希查找平均查找长度的计算;算法设计训练。 三、教学难点:哈希表的构造及解决冲突的算法;算法设计。 四、教学过程:
§7.4 散列表的查找 TKS 3 §7.4.1 散列表的基本概念 1、散列查找法 前面所讨论的查找方法是建立在“比较”的基础上的,查找的效率依赖于查找过程中所进行的比较次数。 理想的情况是希望不经过任何比较就能直接计算出记录的存储地址,从而找到所要查找的记录。 如果能在元素的存储位置和其关键字之间建立某种直接关系,按照这种关系直接由关键字找到相应的记录。这就是散列查找法( Hash Search)的思想。散列查找法又叫杂凑法或散列法。 2、相关概念 (1) 散列函数和散列地址: 在记录的存储位置p和其关键字key之间建立一个确定的对应关系何,使p=H(key),称这个对应关系H为散列函数,p为散列地址。 18:39
(2) 散列表: 0 1 2 3 45 6 7 8 9 10 11121314 15 HT 54 43 18 46 60 75 90 TKS 4 一个有限连续的地址空间,用以存储按散列函数计算得到相应散列地址的数据记录。通常散列表的存储空间是一个一维数组,散列地址是数组的下标。 例1,假设有一批关键字序列18,75,60,43,54,90,46,给定哈希函数H(k)=k%13,存贮区的内存地址从0到15,则可以得到每个关键字的散列地址为: H(18)=18%13=5 H(75)=75%13=10 H(60)=60%13=8 H(43)=43%13=4 H(54)=54%13=2 H(90)=90%13=12 H(46)=46%13=7 根据散列地址,可以将上述7个关键字序列存贮到一个一维数组HT(哈希表或散列表)中,具体表示为: 散列函数 散列地址 散列表 18:39
0 1 2 3 45 6 7 8 9 10 11121314 15 HT 54 43 18 46 60 75 90 TKS 5 其中HT就是散列存贮的表,称为散列表或哈希表。从哈希表中查找一个元素相当方便,例如,查找75,只需计算出H(75)=75%13=10,则可以在HT[10]中找到75。 为了保证哈希表查找得以实现,必须使记录的存放规则和查找规则一致。 (3) 冲突和同义词: 对不同的关键字可能得到同一散列地址,即key1≠key2,而H(keyl)=H(key2),这种现象称为冲突。 具有相同函数值的关键字对该散列函数来说称作同义词,key1与key2互称为同义词。 例2 对C语言某些关键字集合建立一个散列表,关键字集合为: Sl={main,int,float,while,return,break,switch,case,do} 散列表定义为:char HT[26][8]; 18:39
设散列函数为:H(key)=key[0]-‘a’ TKS 6 其中,设key的类型是长度为8的字符数组,根据此散列函数构造的散列表如下。 假设关键字集合扩充为: S2=Sl+{short,default,double,static,for, struct} 若散列函数不变,新加入的六个关键字经过汁算得到: H(short)=H(static)=H(struct)=18 H(default)=H(double)=3 H(for)=5 而18、3和5这几个位置均已存放相应的关键字,这就发生了冲突现象,其中,switch、short、static和struct称为同义词; do、default和double称为同义词,float和for称为同义词。 18:39
4、关键字集合与散列表长度 TKS 7 关键字集合应包含所有可能产生的关键字,在上例中规定关键字为长度不超过8的、字母开头的字母数字串,则标识符取值集合的大小为: 散列表的长度是较有限的,要将多达1012个可能的关键字映射到较有限的地址上,难免产生冲突。通常,散列函数是一个多对一的映射,所以冲突是不可避免的,只能通过选择一个“好”的散列函数使得在一定程度上减少冲突。一旦发生冲突,就必须处理冲突。 所以,散列查找法主要研究以下两方面的问题: (1) 如何构造散列函数; (2) 如何处理冲突。 18:39
§7.4.2 散列函数的构造方法 TKS 8 1、构造散列函数要考虑的因素 (1)散列表的长度; (2)关键字的长度; (3)关键字的分布情况; (4)计算散列函数所需的时间; (5)记录的查找频率。 2、构造一个“好”的散列函数应遵循以下两条原则 (1)函数计算要简单,每一关键字只能有一个散列地址与之对应; (2)函数的值域需在表长的范围内,计算出的散列地址的分布应均匀(经散列函数映像到地址集合中任何一个地址的概率是相等的),尽可能减少冲突。 3、构造散列函数几种常用的方法 (1) 数字分析法 18:39
对散列表中可能出现的关键字是事先知道的,取关键字的若干位组成散列地址。对散列表中可能出现的关键字是事先知道的,取关键字的若干位组成散列地址。 …… 8 1 3 4 6 5 3 2 8 1 3 7 2 2 4 2 8 1 3 8 7 4 2 2 8 1 3 0 1 3 6 7 8 1 3 2 2 8 1 7 8 1 3 3 8 9 6 7 8 1 3 5 4 1 5 7 8 1 3 6 8 5 3 7 8 1 4 1 9 3 5 5 …… ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ 关键字 函数地址 (关键字)2 0010000 1210000 1440000 1370400 4310541 0100 1100 1200 1160 2061 010 210 440 370 310 TKS 9 如表长为100,关键字中的一部分如右: 可取第④位到第⑦位中的任意两位组成散列地址如右。 (2) 平方取中法 以关键字的平方值的中间几位作为存储地址。 求“关键字的平方值”的目的是“扩大差别”, 同时平方值的中间各位又能受到整个关键字中各位的影响。 随机给出一些关键字,取平方后的第2到4位为函数地址如右。 18:39
(3) 折叠法 TKS 10 将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希函数地址,称为折叠法。 有两种叠加处理的方法:移位叠加和边界叠加。 移位叠加:将分割后的每一部分的最低位对齐,然后相加。 边界叠加:从一端向另一端沿分割界来回折叠,然后对齐相加。 例如,设某人身份证号码为 430104681015355, 5355 1018 1046 +) 034 —————————————— 7453 H(430104681015355)=7453 此方法适合于: 关键字的数字位数特别多。 5355 8101 1046 +) 430 —————————————— 14932 H(430104681015355)=4932 移位叠加 边界叠加 18:39
(4) 除留余数法 如对关键字(22,41,53,46,30,13,01,67),表长为11,散列函数为: ,则有: TKS 11 表长为m,则 H(key) = key MOD p p≤m 选择好p很重要,否则关键字相同的地址h(key)可能会很多。 除留余数法计算简单,适用范围广,是一种最常使用的方法。这种方法的关键是选取较理想的p值,使得每一个关键字通过该函数转换后映射到散列空间上任一地址的概率都相等,从而尽可能减少发生冲突的可能性。 p 应为不大于 m的素数。 通常,此方法用于对长度不等的关键字构造散列函数。 18:39
§7.4.3 处理冲突的方法 TKS 12 1、处理冲突的目标 由于散列函数是关键字的一个映像,这就不可避免产生冲突。 处理冲突是为地址冲突的关键字记录找到一个“空”的散列地址2、处理冲突的方法 (1) 开放定址法 ① 开放地址法的基本思想 把记录都存储在散列表数组中,当某一记录关键字key的初 始散列地址H0=H(key)发生冲突时,以H0为基础,采取合适方法计算得到另一个地址H1,如果H1仍然发生冲突,以H1为基础再求下一个地址H2,若H2仍然冲突,再求得H3。依次类推,直至Hk不发生冲突为止,则Hk为该记录在表中的散列地址。 这种方法在寻找“下一个”空的散列地址时,原来的数组空间对所有的元素都是开放的,所以称为开放地址法。 18:39
通常把寻找“下一个”空位的过程称为探测。 TKS 13 这样为产生冲突的地址 H(key) 求得一个地址序列: H0, H1, H2, …, Hk1≤k≤m-1 H0 = H(key) Hi = ( H(key) + di) MOD mi=1, 2, …, k 其中H(key)为散列函数,m为散列表长度,di为增量序列 根据di取值的不同方法,可以分为以下三种探测方法。 ② 线性探测法 di= 1,2,3,…,m-1 假设散列表的地址为0~m-1,则散列表的长度为m。若一个关键字在地址d处发生冲突,则依次探查d+1,d+2,…,d+m-1(当达到表尾m-1时,又从0,1,2,…. 开始探查)等地址,直到找到一个空闲位置来装冲突处的关键字。 设发生冲突时的地址为d0=H(k),则探查下一位置的公式为: di=(di-1+1)%m (1≤i≤m-1) 18:39
③ 二次探测法 0 1 2 3 4 5 6 7 8 9 10 TKS 14 di=12, -12, 22, -22,…, ④伪随机探测法 di=伪随机数序列 如:di+1=(q×di+p) mod m ▲示例: 关键字集合{19,01,23,14,55,68,11,82,36},设散列函数为 H(key) = key MOD 11 ( 表长=11 ) 采用线性探测法处理冲突: 55 01 23 14 68 11 82 36 19 1 1 2 1 3 6 2 5 1 18:39
采用二次探测法处理冲突: 0 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 10 TKS 15 关键字集合{19,01,23,14,55,68,11,82,36},设哈希函数为 H(key) = key MOD 11 ( 表长=11 ) 55 19 01 23 14 36 82 68 11 1 1 2 1 2 1 4 1 3 采用伪随机探测再散列处理冲突:(伪随机序列:9,5,13,3…) 55 01 68 14 11 36 82 19 23 1 1 1 1 2 2 2 1 2 18:39
⑤ 三种处理方法的优缺点 TKS 16 Ⅰ 线性探测法 优点:只要散列表未填满,总能解决冲突; 缺点:有可能产生“二次聚集”。 Ⅱ 二次探测法和伪随机探测法 优点:可以避免“二次聚集”现象; 缺点:不能保证一定找到不发生冲突的地址。(2) 链地址法 ① 链地址法的基本思想 把具有相同散列地址的记录放在同一个单链表中,称为同义词链表。有m个散列地址就有m个单链表,同时用数组HT[O…m-1]存放各个链表的头指针,凡是散列地址为i的记录都以结点方式插入到以HT[i]为头结点的单链表中。 18:39
② 例 已知一组关键字为(19,14,23,01,68,20,84,27,55,11,10, 0 1 2 3 4 5 6 7 8 9 10 11 12 ∧ ∧ 01 01 01 55 14 27 23 23∧ 11∧ 68∧ 14 14∧ 68 79 14 19 27 19∧ 20∧ 10 84 ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ TKS 17 79),设散列函数h(key)=key mod 13。用链地址法处理冲突,构造这组关键字的散列表。 18:39
§7.4.4散列表的查找 0 1 2 3 4 5 6 7 8 9 10 55 01 23 14 68 11 82 36 19 1 1 2 1 3 6 2 5 1 TKS 18 1、线性探测法解决冲突的查找 (2)算法思想 ① 散列表初始化操作 Ⅰ 申请散列表空间 Ⅱ 散列表空间内容置空 Ⅲ 散列表长度置0 ②设置散列函数 ③ 查找和建立散列表操作 对输入的记录: Ⅰ 计算散列地址 Ⅱ 按线性探测法对关键字进行查找 Ⅲ对查找失败的记录进行插入操作,并将散列表长度增1 (1)散列表的存储结构 struct helem // 元素结构 { int data; int suc; }; struct htable // 表结构 { helem *elem; int count; }; 18:39
(3)查找算法 0 1 2 3 4 5 6 7 8 9 10 55 01 23 14 68 11 82 36 19 1 1 2 1 3 6 2 5 1 TKS 19 int searchhash(htable ht,int key,int *p,int *c) { *c=1;*p=hash(key); while(ht.elem[*p].data!=-1&&ht.elem[*p].data!=key) { *p=(*p+1)%n; (*c)++; } if(key==ht.elem[*p].data) return 1; else return 0; } 算法 7.10 线性探测法解决 冲突的查找和建表算法 S21_1 (4)插入算法 void inserthash(htable ht,int key) {int c=0,p; if(searchhash(ht,key,&p,&c)) printf("%d alrealy exists!",key); else {ht.elem[p].data=key;ht.elem[p].suc=c;ht.count++;} } 18:39
2、链地址法解决冲突的查找 TKS 20 (1)散列表的存储结构 ①元素结构 typedef struct node { int data; struct node *next; } hnode,*link; (2)算法思想 ① 散列表初始化操作 Ⅰ 申请散列表表头指针数组空间 Ⅱ 散列表表头数组中的均置为空(NULL) Ⅲ 散列表表头数组长度置0 ②设置散列函数 ③ 查找和建立散列表操作 对输入的记录: Ⅰ 计算散列地址 Ⅱ 按链地址法对关键字进行查找 Ⅲ对查找失败的记录进行插入操作,并将散列表长度增1 ② 表结构 struct htable { hnode **elem; int count; }; 18:39
(3) 查找算法 0 1 2 3 4 5 6 7 8 9 10 11 12 ∧ ∧ 01 14 55 01 01 84 19 68∧ 68 14 14 11∧ 23∧ 23 27 79 14∧ 10 20∧ 19∧ 27 ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ TKS 21 int searchhash(htable ht, int key,int *h) { hnode *p; *h=hash(key);p=ht.elem[*h]; while(p!=NULL&&p->data!=key) p=p->next; if(key==p->data) return 1; else return 0; } 18:39
③ 插入算法 0 1 2 3 4 5 6 7 8 9 10 11 12 ∧ ∧ 01 01 01 55 14 27 23 23∧ 11∧ 68 14 14∧ 68∧ 27 79 14 19 19∧ 20∧ 10 84 ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ TKS 22 补充算法 链地址法解决 冲突的查找和建表算法 S21_2 void inserthash(htable *ht,int key) { link p;int h; if(searchhash(*ht,key,&h)) printf("%d alrealy exists!",key); else { p=new hnode; if(p==NULL) exit(0); p->data=key; p->next=ht->elem[h]; ht->elem[h]=p; ht->count++; } } 18:39
3、查找算法分析 TKS 23 (1) 查找过程分析 由于难免产生冲突,查找过程仍是一个给定值和关键字进行比较过程。 (2) 查找过程比较关键字的个数决定下列三个因数:散列函数,处理冲突的方法和散列表的装填因子; (3)散列表的装填因子: (4) 影响平均查找长度的因素 散列函数的“好坏”首先影响出现冲突的频繁程度。 设散列函数是“均匀”的,则影响平均查找长度的因素只有两个:处理冲突的方法和装填因子 。 18:39
4、示例 0 1 2 3 4 5 6 7 8 9101112131415 关键字 14 01 68 27 55 19 20 84 79 23 11 10 查找成功比较次数 1 2 1 4 3 1 1 3 9 1 1 3 查找不成功比较次数 1 13 12 11 10 9 8 7 6 5 4 3 2 TKS 24 例1 已知一组关键字为(19,14,23,01,68,20,84,27,55,11,10,79),h(key)=key mod 13。用线性探测再散列处理冲突,构造散列表,求查找成功与查找不成功的比较次数。 表长为16的哈希表如下: 查找成功:ASL=1/12×(1*6+2+3*3+4+9)=2.5 查找不成功:ASL=1/13×(1+13)×13/2=7 18:39
例2 已知一组关键字为(19,14,23,01,68,20,84,27,55,11,10,79),h(key)=key mod 13。用链地址法再散列处理冲突,构造哈希表,求查找成功与查找不成功的比较次数。 0 1 2 3 4 5 6 7 8 9 10 11 12 ∧ ∧ 01 14 55 01 01 84 19 68∧ 68 23 14 79 14 11∧ 14∧ 27 23∧ 10 20∧ 19∧ 27 ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ TKS 25 查找成功: ASL=1/12×(1*6+2*4+3+4)=1.75 查找不成功: ASL=1/13×(1+5+1+3+1+1+3+2+1+1+3+2+1) =25/13=1.92 18:39
5、用几种不同方法处理冲突时散列表的平均查找长度5、用几种不同方法处理冲突时散列表的平均查找长度 处理冲突的方法 平均查找长度 查找成功 查找失败 线性探测法 二次探测法 伪随机探测法 链地址法 TKS 26 五、作业: ? 1、书面作业:P204:1中(12)~(15) P204:2中(5)~(7) 2、实践:实验三、二叉排序树 18:39