630 likes | 753 Views
第 9 章 查找. 1. 2. 3. 查找的概念. 顺序表查找. 索引查找. 4. 散列查找. 5. B 树查找. 主要内容. 9.1 查找的概念. 查找:也叫检索,是根据给定的某个值,在表中确定一个关键字等于给定值的记录或数据元素 关键字:是数据元素中某个数据项的值,它可以标识一个数据元素 查找方法评价 查找速度 占用存储空间多少 算法本身复杂程度. 9.1 查找的概念. 查找方法评价
E N D
1 2 3 查找的概念 顺序表查找 索引查找 4 散列查找 5 B树查找 DS 主要内容
DS 9.1 查找的概念 • 查找:也叫检索,是根据给定的某个值,在表中确定一个关键字等于给定值的记录或数据元素 • 关键字:是数据元素中某个数据项的值,它可以标识一个数据元素 • 查找方法评价 • 查找速度 • 占用存储空间多少 • 算法本身复杂程度
DS 9.1 查找的概念 • 查找方法评价 • 平均查找长度ASL(Average Search Length):为确定记录在表中的位置,需和给定值进行比较的关键字的个数的期望值叫查找算法的平均查找长度
DS 9.2 顺序表查找 • 顺序表:是指集合或线性表的顺序存储结构。 • 表示:一维数组A,元素类型为ElemType • 组成:关键字key域(用KeyType表示)和其他一些数据域 • 大小:整型常量MaxSize • 元素个数:n • 比较:元素类型ElemType可以是任何简单类型,此时关键字key域就是该元素类型本身,元素的关键字域A[i].key同给定关键字K的比较就变成元素整体值A[i]同K的比较
DS 9.2 顺序表查找 • 9.2.1 顺序查找 • 9.2.2 二分查找
DS 9.2.1 顺序查找 • 顺序查找:又称线性查找,是一种最简单和最基本的查找方法。 • 查找过程:从表的一端开始逐个进行记录的关键字和给定值的比较 • 算法描述: int Seqsch(ElemType A[],int n,KeyType K) { for(int i=0;i<n;i++) if(A[i].key==K) break; if(i<n) return i; else return -1; }
DS 9.2.1 顺序查找 • 改进的算法:在表的尾端设置一个“岗哨”,其值为最后一个元素的值,可省略下标越界的判断。 int Seqsch1(ElemType A[],int n,KeyType K) { A[n].key=K; //设置岗哨 for(int i=0; ;i++) if(A[i].key==K) break; if(i<n) return i; else return -1; }
DS 9.2.1 顺序查找 • 顺序查找的平均查找长度ASL
DS 9.2.2 二分查找 • 二分查找:又称折半查找、对分查找。 • 查找过程:每次将待查记录所在区间缩小一半 • 适用条件:采用顺序存储结构的有序表 • 算法实现 • 设表长为n,low、high和mid分别指向待查元素所在区间的上界、下界和中点,k为给定值 • 初始时,令low=0,high=n-1,mid=(low+high)/2 • 让k与mid指向的记录比较 • 若k==r[mid].key,查找成功 • 若k<r[mid].key,则high=mid-1 • 若k>r[mid].key,则low=mid+1 • 重复上述操作,直至low>high时,查找失败
DS 9.2.2 二分查找 • 算法描述—递归 int Binsch(ElemType A[],int low,int high,KeyType K) { if(low<=high) { int mid=(low+high)/2; if(K==A[mid].key) return mid; else if(K<A[mid].key) return Binsch(A,low,mid-1,K); else return Binsch(A,mid+1,high,K); } else return -1; }
DS 9.2.2 二分查找 • 算法描述—非递归 int Binsch(ElemType A[], int n, KeyType K) { int low=0, high=n-1; //上界和下界 while(low<=high) { int mid=(low+high)/2; //求中间点 if(K==A[mid].key) return mid; //查找成功 else if(K<A[mid].key) high=mid-1; //修改上界 else low=mid+1; //修改下界} return -1; //查找失败返回-1 }
DS 9.2.2 二分查找 key=64的查找过程: A low low high high mid mid mid low指示查找区间的下界 high指示查找区间的上界 mid = (low+high)/2
56 80 19 64 05 88 21 75 92 13 37 DS 9.2.2 二分查找 • 判定树:把树中的每个根结点对应当前查找区间的中点元素,它的左子树和右子树分别对应该区间的左子表和右子表。 • 例:(05,13,19,21,37,56,64,75,80,88,92)
DS 9.2.2 二分查找 • 算法评价 • 有n个结点的判定树的深度为log2n+1 • 折半查找法在查找过程中进行的比较次数最多不超过其判定树的深度 • 折半查找的ASL
DS 9.3 索引查找 • 9.3.1 索引的概念 • 9.3.2 索引查找算法 • 9.3.3 分块查找*
DS 9.3.1 索引的概念 • 索引查找:又称分级查找。首先把一个线性表(主表)或集合按照一定的函数关系或条件划分成若干个逻辑上的子表,为每个子表分别建立一个索引项,由所有这些索引项构成主表的一个索引表。 • 查找过程: • 由索引确定记录所在区间; • 在顺序表的某个区间内进行查找。 • 注意: • 索引顺序查找的过程也是一个“缩小区间”的查找过程。 • 索引可以根据查找表的特点来构造。
DS 9.3.1 索引的概念 • 索引项的组成 • 索引值域(index):存储标识对应子表的索引值,相当于关键字,来唯一标识一个索引项和一个子表 • 子表的开始位置域(start):存储对应子表的第一个元素的存储位置,从此位置可以依次访问到子表中的所有元素 • 子表长度域(length):存储对应子表的元素个数
DS 9.3.1 索引的概念 • 索引项的类型和顺序存储的索引表的类型定义: struct IndexItem { IndexKeyType index; int start; int length; }; typedef IndexItem indexlist[ILMaxSize];
职工号 姓名 单位 职称 工资 出生日期 JS001 王大明 计算机 教授 680.00 42/05/13 JS002 吴 进 计算机 讲师 440.00 63/07/25 JS003 邢怀学 计算机 讲师 460.00 60/12/08 JS004 朱小五 计算机 副教授 550.00 48/06/09 DZ001 赵 利 电子 助教 380.00 68/05/24 DZ002 刘 平 电子 讲师 480.00 59/05/30 DZ003 张 卫 电子 副教授 600.00 46/02/24 JJ001 安晓军 机械 讲师 450.00 62/11/17 JJ002 赵京华 机械 讲师 440.00 64/04/28 HG001 孙 亮 化工 教授 720.00 43/06/03 HG002 陆 新 化工 副教授 580.00 56/02/19 HG003 王 方 化工 助教 400.00 62/06/20
DS 9.3.1 索引的概念 • 若以每个记录的职工号作为关键字,则此线性表(假定用LA表示)可简记为: LA=(JS001,JS002,JS003,JS004,DZ001, DZ002,DZ003,JJ001,JJ002,HG001, HG002,HG003) • 若按照单位数据项的值(或关键字中的前两位字符)对线性表LA进行划分,使得具有相同值的元素在同一个子表中,则得到的四个子表分别为: JS=(JS001,JS002,JS003,JS004) DZ=(DZ001,DZ002,DZ003) JJ=(JJ001,JJ002) HG=(HG001,HG002,HG003)
DS 9.3.1 索引的概念 • 索引表b1
DS 9.3.1 索引的概念 • 稀疏索引:在索引存储中,若索引表中的每个索引项对应多条记录,则称为稀疏索引。 • 稠密索引:若每个索引项唯一对应一条记录,则称为稠密索引。 • 在文件系统中索引的选择 • 主文件是无序:一级索引(主文件的索引)必须使用稠密索引,并且通常使索引表按关键字有序 • 主文件是有序:一级索引应采用稀疏索引,每个索引项对应连续若干条记录,每个索引项中的索引值要大于等于所对应一组记录的最大关键字,同时要小于下一个索引项所对应一组记录的最小关键字。显然,这种稀疏索引也是按索引值有序的。 • 二级或二级以上索引:相应的索引表均应采用稀疏索引。
DS 9.3.2 索引查找算法 • 索引查找是在索引表和主表上进行的查找。 • 查找的过程:首先根据给定的索引值K1,在索引表中查找出索引值等于K1的索引项,以确定对应子表在主表中的开始位置和长度,然后再根据给定的关键字K2,在对应的子表中查找出关键字等于K2的元素(结点)。
DS 9.3.2 索引查找算法 • 设数组A是具有mainlist类型的一个主表,数组B是具有indexlist类型在主表A上建立的一个索引表,m为索引表B的实际长度,K1和K2分别为给定待查找的索引值和关键字,(类型应分别为索引值域和主表关键字域的类型)并假定每个子表采用顺序存储。算法描述为:
DS 9.3.2 索引查找算法 • 索引顺序查找的平均查找长度 =查找“索引”的平均查找长度+ 查找“顺序表”的平均查找长度 • 假定索引表的长度为m,相应子表的长度为s,则平均查找长度为: 假设:
DS 9.3.3*分块查找 • 属于索引查找,其对应的索引表为稀疏索引。要求: • 索引表是按索引值递增(或递减)有序的; • 主表中的关键字域和索引表中的索引值域具有相同的数据类型,即为关键字所属的类型。
0 1 2 Key=40 索引表 • 72 98 • 0 5 10 • 4 5 3 Index Start length 主表 15 26 18 34 36 72 40 57 43 86 93 98 0 1 2 3 4 5 6 7 8 9 10 11 12 13
DS 9.4 散列查找 • 9.4.1 散列的概念 • 9.4.2 散列函数 • 9.4.3 处理冲突的方法 • 9.4.4 散列表的运算
DS 9.4.1 散列的概念 • 为每年招收的 1000 名新生建立一张查找表,关键字为学号,其值的范围为 xx000 ~ xx999 (前两位为年份)。若以下标为000 ~ 999 的顺序表表示之。则查找过程可以简单进行:取给定值(学号)的后三位,不需要经过比较便可直接从顺序表中找到待查关键字。 • 但是,对于动态查找表而言, • 表长不确定; • 在设计查找表时,只知道关键字所属范围,而不知道确切的关键字。
关键字 集合 存储地址 集合 hash DS 9.4.1 散列的概念 • 因此,在记录的存储地址和它的关键字之间建立一个确定的对应关系;这样,不经过比较,一次存取就能得到所查元素的查找方法. • 哈希函数:在记录的关键字与记录的存储地址之间建立的一种对应关系叫哈希函数。 • 哈希函数是一种映象,是从关键字空间到存储地址空间的一种映象 • 哈希函数可写成:addr(ai)=H(ki) • ai是表中的一个元素 • addr(ai)是ai的存储地址 • ki是ai的关键字
例 30个地区的各民族人口统计表 编号 地区别 总人口 汉族 回族…... 1 北京 2 上海 …... …... DS 9.4.1 散列的概念 • 哈希表:又叫散列表,应用哈希函数,由记录的关键字确定记录在表中的地址,并将记录放入此地址,这样构成的表叫~ • 哈希查找:又叫散列查找,利用哈希函数进行查找的过程叫~ 以地区别作关键字,取地区名称第一个拼音字母的序号 作哈希函数:H(Beijing)=2 H(Shanghai)=19 H(Shenyang)=19 以编号作关键字, 构造哈希函数:H(key)=key H(1)=1 H(2)=2
DS 9.4.1 散列的概念 从例子可见: • 哈希函数只是一种映象,所以哈希函数的设定很灵活,只要使任何关键字的哈希函数值都落在表长允许的范围之内即可 • 冲突:key1key2,但H(key1)=H(key2)的现象叫~ • 同义词:具有相同函数值的两个关键字,叫该哈希函数的~ • 哈希函数通常是一种压缩映象,所以冲突不可避免,只能尽量减少;同时,冲突发生后,应该有处理冲突的方法
DS 9.4.1 散列的概念 • 影响冲突大小的因素 • 装填因子:散列表中已存入的元素数n与散列表空间大小m的比值,即α=n/m 。当α越小,冲突的可能性就越小;当α越大,冲突的可能性就越大。通常控制在0.6~0.9范围内为宜。 • 与采用的散列函数有关 • 解决冲突的方法有关
DS 9.4.1 散列的概念 • 桶:在散列存储中,每个散列地址对应的存储空间称为一个桶。 • 一个桶可以为一个单元,对应存储一个元素;也可以为若干个单元,对应存储若干个元素。 • 当一个桶为多个单元时,只有都被占满后才发生冲突。
DS 9.4.2 散列函数 • 构造散列函数的目标:使散列地址尽可能均匀地分布在散列空间上,同时是计算尽可能简单,以节省计算时间。 • 构造散列函数的方法 • 直接定址法 • 构造:取关键字或关键字的某个线性函数作哈希地址,即H(key)=key 或 H(key)=a·key+b • 特点 • 直接定址法所得地址集合与关键字集合大小相等,不会发生冲突 • 实际中能用这种哈希函数的情况很少
DS 9.4.2 散列函数 • 除留余数法 • 构造:取关键字被某个不大于哈希表表长m的数p除后所得余数作哈希地址,即H(key)=key MOD p,pm • 特点 • 简单、常用,可与其他方法结合使用 • p的选取很重要;p选的不好,容易产生同义词。p 应为不大于 m 的素数或是不含 20 以下的质因子
DS 9.4.2 散列函数 • 为什么要对 p 加限制? • 给定一组关键字:12, 39, 18, 24, 33, 21,若取p=9, 则他们对应的哈希函数值将为: 3, 3, 0, 6, 6, 3 可见,若 p 中含质因子 3,则所有含质因子 3 的关键字均映射到“3 的倍数”的地址上,从而增加了“冲突”的可能。
DS 9.4.2 散列函数 • 数字分析法 • 构造:对关键字进行分析,取关键字的若干位或其组合作哈希地址 • 适于关键字位数比哈希地址位数大,且可能出现的关键字事先知道的情况
….. 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 6 8 5 3 7 8 1 4 1 9 3 5 5 ….. DS 9.4.2 散列函数 • 例:有80个记录,关键字为8位十进制数,哈希地址为2位十进制数 分析: 只取8 只取1 只取3、4 只取2、7、5 数字分布近乎随机 所以:取任意两位或两位 与另两位的叠加作哈希地址
DS 9.4.2 散列函数 • 平方取中法 • 构造:取关键字平方后中间几位作哈希地址 • 适于关键字中的每一位都有某些数字重复出现频度很高的现象 • 折叠法 • 构造:将关键字分割成位数相同的几部分,然后取这几部分的叠加和(舍去进位)做哈希地址 • 种类 • 移位叠加:将分割后的几部分低位对齐相加 • 间界叠加:从一端沿分割界来回折送,然后对齐相加 • 适于关键字位数很多,且每一位上数字分布大致均匀情况
DS 9.4.2 散列函数 例 关键字为 :0442205864,哈希地址位数为4 5 8 6 4 5 8 6 4 0 2 2 4 4 2 2 0 0 4 0 4 移位叠加 间界叠加 6 0 9 2 1 0 0 8 8 H(key)=6092 H(key)=0088
DS 9.4.3 处理冲突的方法 • “处理冲突” 的实际含义是:为产生冲突的地址寻找下一个哈希地址。 • 处理冲突的方法: • 开放定址法 • 链接法
DS 9.4.3 处理冲突的方法 • 开放定址法 • 方法:从发生冲突的那个单元开始,依照一定的次序,从散列表中查找出一个空闲的存储单元,把发生冲突的待插入元素存入到该单元中的一类处理冲突的方法。即Hi=(H(key)+di)MOD m,i=1,2,……k(km-1) 其中:H(key)——哈希函数 m——哈希表表长 di——增量序列
DS 9.4.3 处理冲突的方法 • 分类: • 线性探测再散列:di=1,2,3,……m-1 • 二次探测再散列:di=1², -1², 2², -2², 3², …… ±k² (km/2) • 双散列函数探测 :di是一组伪随机数列或者 di=(di-1+H2(key))%m (又称随机探测再散列)
0 1 2 3 4 5 6 7 8 9 10 60 17 29 DS 9.4.3 处理冲突的方法 • 例:表长为11的哈希表中已填有关键字为17,60,29的记录,H(key)=key MOD 11,现有第4个记录,其关键字为38,按两种处理冲突的方法,将它填入表中 38 38 38 (1) H(38)=38 MOD 11=5 冲突 H1=(5+1) MOD 11=6 冲突 H2=(5+2) MOD 11=7 冲突 H3=(5+3) MOD 11=8 不冲突 (2) H(38)=38 MOD 11=5 冲突 H1=(5+1²) MOD 11=6 冲突 H2=(5-1²) MOD 11=4 不冲突
0 ^ ^ 1 2 ^ 3 ^ 4 ^ 5 ^ ^ 6 7 ^ 10 84 1 14 27 79 68 11 23 19 20 55 8 ^ 9 ^ ^ 10 11 ^ ^ 12 DS 9.4.3 处理冲突的方法 • 链地址法:将所有关键字为同义词的记录存储在一个单链表中,并用一维数组存放头指针 • 例:已知一组关键字(19, 14, 23, 1, 68, 20, 84, 27, 55,11,10,79),哈希函数为:H(key)=key MOD 13,用链地址法处理冲突
给定k值 计算H(k) Y N 此地址为空 Y 查找失败 N 关键字==k 查找成功 按处理冲突 方法计算Hi DS 9.4.3 处理冲突的方法 • 散列查找的过程:
DS 9.4.3 处理冲突的方法 • 散列表查找的分析:从查找过程得知,哈希表查找的平均查找长度实际上并不等于零。决定哈希表查找的ASL的因素: • 选用的哈希函数; • 选用的处理冲突的方法; • 哈希表饱和的程度,装载因子α=n/m 值的大小(n记录数,m表的长度)
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 例 已知一组关键字(19,14,23,1,68,20,84,27,55,11,10,79)。哈希函数为:H(key)=key MOD 13, 哈希表长为m=16,设每个记录的查找概率相等 (1) 用线性探测再散列处理冲突,即Hi=(H(key)+di) MOD m 14 1 68 27 55 19 20 84 79 23 11 10 H(11)=11 H(19)=6 H(14)=1 H(10)=10 冲突,H1=(10+1)MOD16=11 冲突,H2=(10+2)MOD16=12 H(23)=10 H(1)=1 冲突,H1=(1+1) MOD16=2 H(79)=1 冲突,H1=(1+1)MOD16=2 冲突,H2=(1+2)MOD16=3 冲突,H3=(1+3)MOD16=4 冲突,H4=(1+4)MOD16=5 冲突,H5=(1+5)MOD16=6 冲突,H6=(1+6)MOD16=7 冲突,H7=(1+7)MOD16=8 冲突,H8=(1+8)MOD16=9 H(68)=3 H(20)=7 H(84)=6 冲突,H1=(6+1)MOD16=7 冲突,H2=(6+2)MOD16=8 H(27)=1 冲突,H1=(1+1)MOD16=2 冲突,H2=(1+2)MOD16=3 冲突,H3=(1+3)MOD16=4 H(55)=3 冲突,H1=(3+1)MOD16=4 冲突,H2=(3+2)MOD16=5 ASL=(1*6+2+3*3+4+9)/12=2.5