440 likes | 599 Views
7 查找 (1). 教学目标. 1 、掌握查找的概念,熟练掌握顺序表和有序表(折半查找)的查找算法及其性能分析方法; 2 、掌握二叉排序树的概念,熟练掌握二叉排序树的构造和查找算法及其性能分析方法; 3 、掌握二叉排序树的插入算法,二叉排序树的删除方法; 4 、掌握平衡二叉树的概念,掌握平衡化二叉树的方法; 5 、掌握哈希查找的概念,掌握哈希函数(除留余数法)的构造。 6 、掌握用线性探测法和链地址法解决冲突的方法,并能进行查找长度的计算。 7 、熟悉标准模版库 STL 中的相关知识。. 教学内容. 7.1 静态查找表 7.2 动态查找表
E N D
教学目标 1、掌握查找的概念,熟练掌握顺序表和有序表(折半查找)的查找算法及其性能分析方法; 2、掌握二叉排序树的概念,熟练掌握二叉排序树的构造和查找算法及其性能分析方法; 3、掌握二叉排序树的插入算法,二叉排序树的删除方法; 4、掌握平衡二叉树的概念,掌握平衡化二叉树的方法; 5、掌握哈希查找的概念,掌握哈希函数(除留余数法)的构造。 6、掌握用线性探测法和链地址法解决冲突的方法,并能进行查找长度的计算。 7、熟悉标准模版库STL中的相关知识。
教学内容 7.1 静态查找表 7.2 动态查找表 7.3 哈希表(散列表) 7.4 查找与标准模版库STL
问题一 高考招生工作结束了,学校在第一时间公布了已经被录取的学生的准考证号码和姓名。但由于数量太多,肉眼很难查看。因此,学校的招生网往往会允许考生根据自己的准考证号码, 来查看是否已被录取。 网站一般是如何根据输入的准考证号码来实现快速查找的呢?
问题二 手机的使用者会经常更新手机中的通讯录。当接听或拨打朋友的电话时,会检查这个朋友的号码是否已经存在自己手机的通讯录中,若有必要就会将该号码添加到通讯录中。 系统如何快速查找某个号码,以及是如何在通讯录中添加号码的?查找和添加的效率如何?
相关知识一 什么是查找表 ? 由相同类型的数据元素(或记录)构成的集合称作查找表。其元素之间无明确的关系。 问题一的已录取的学生信息和问题二的通讯录均属查找表。
相关知识二 对查找表经常进行的操作: 1)查询某个数据元素是否在查找表中; 2)在查找表中插入一个数据元素; 3)从查找表中删去某个数据元素。 问题一和问题二均需要进行查找。 问题二还需要插入或删除。
相关知识三 查找表的一般分类: 静态查找表:仅作查询或检索操作的查找表。 动态查找表:有时在查询之后,还需要将“查找失败”的数据元素插入到查找表中;或者从查找表中删除其指定的数据元素。 问题一和问题二分属静态查找表和动态查找表。
相关知识四 什么是关键字? 是数据元素(或记录)中某个数据项的值,用以标识某个数据元素(或记录)。 问题一的准考证号码属于关键字。 问题二的电话号码属于关键字。
相关知识五 查找成功和失败的依据是什么? 在查找表中确定是否存在一个数据元素,该元素的关键字等于给定的某个值。 若查找表中存在这样的元素,则“查找成功”。否则“查找失败”。 问题一:准考证号出现在查找表中表示成功。 问题二:电话号码出现在通讯录中表示成功。
相关知识六 如何进行查找,效率如何? 查找的方法取决于查找表的结构。采用不同的存储结构将导致不同的查找效率。 因此,采用合适的数据结构来表示查找表是非常重要的。 问题一的准考证号可以用(有序)数组存放。 问题二的电话号码可以用二叉(排序)树存放。
7.1 静 态 查 找 表 一、顺序查找表 二、有序查找表
ADT StaticSearchTable { 具有相同特性的数据元素的集合。含有类型相同的关键字,可唯一标识数据元素。 数据对象D: 数据元素同属一个集合。 数据关系R: Create(&ST, n); //构造 Destroy(&ST); // 销毁 Search(ST, key); // 查询 Traverse(ST, Visit()); // 遍历 基本操作 P: } ADT StaticSearchTable
一、顺序查找表 通常以顺序表或线性链表表示静态查找表。
数据元素的类型定义为: struct ElemType{ keyType key; // 关键字 … … // 其它属性可选 }; 静态查找表的顺序存储结构为 struct SSTable{ ElemType *elem; // 元素存储空间基址,0号单元留空 int length; // 表的长度 };
顺序表的查找过程: k=7 ST.elem 假设给定值 e=64, 要求 ST.elem[k] = e, 问: k = ?
int Location(SSTable ST, KeyType e) // 正向查找 { k = 1; while ( k<=ST.length && ST.elem[k]!=e) k++; if ( k<= ST.length) return k; else return 0; }//Location
从尾端往前查,并且初始设置ST.elem[0]=key(称作哨兵pivot)从尾端往前查,并且初始设置ST.elem[0]=key(称作哨兵pivot) i i ST.elem 64 key=64 i i ST.elem 60 key=60
int Search_Seq(SSTable ST, KeyType keyV) { // 在顺序表ST中顺序查找其关键字等于key的数据元素。 // 若找到,则返回该元素在表中的位置,否则为0。 ST.elem[0].key = keyV; // “哨兵” // 从后往前找 for (i=ST.length; ST.elem[i].key!=keyV; i--) ; return i; // 找不到时,i为0 } // Search_Seq 本方法的时间复杂度和前者相同,但由于避免了下标越界的比较,实际效果还是不错的。
分析顺序查找的时间性能 定义:查找算法的平均查找长度 (Average Search Length) 为确定记录在查找表中的位置,需和给定值进行比较的关键字个数的期望值 其中: n为表长,Pi为查找表中第i个记录的概率, 且 , Ci为找到该记录时,已经的比较次数。
对顺序表而言,Ci = n-i+1 (逆向查找) ASL = nP1 +(n-1)P2 + …… +2Pn-1+Pn 在等概率查找的情况下, 顺序表查找的平均查找长度为: 这里只考虑查找成功的代价 Sequence Search
若考虑查找不成功时的情况: 查找不成功时的比较次数:n+1 假设有一半情况查找失败,则兼顾查找成功和失败平均查找长度为: 今后,一般仅讨论查找成功时的平均查找长度和查找不成功时的比较次数,但哈希表例外。 此处说明查找失败需要花费更多的代价
二、有序查找表 顺序查找表的查找算法简单,但平均查找长度较大,效率较低。 若以有序表表示静态查找表,则查找过程可以基于“折半”进行。(又称二分查找)
折半查找的基本思想 每比较一次,查找区间缩小一半。若最后的查找区间为空,则查找失败。 • 折半查找:先计算查找区域正中间对象的下标mid,用其关键字与给定值x比较: • elem[mid].key = x,查找成功,结束查找; • elem[mid].key > x,把查找区间缩小到表的前半部分,再继续进行折半查找; • elem[mid].key < x,把查找区间缩小到表的后半部分,再继续进行折半查找。
例如: key=64的查找过程如下: ST.length ST.elem low low high high mid mid mid low指示查找区间的下界 high指示查找区间的上界 mid= (low+high)/2
int Search_Bin ( SSTable ST, KeyType keyV) { low = 1; high = ST.length; // 置区间初值 while (low <= high) { mid = (low + high) / 2; if (keyV == ST.elem[mid].key ) return mid; // 找到待查元素 else if (keyV < ST.elem[mid].key) high = mid - 1; // 继续在前半区间进行查找 else low = mid + 1; // 继续在后半区间进行查找 } return 0;// 顺序表中不存在待查元素 }// Search_Bin
分析折半查找性能 先看一个具体的情况,假设:n=11 3 4 2 3 4 1 3 4 2 3 4 判定树 6 3 9 4 7 1 10 5 8 2 11
显然,表长为 n 的折半查找的判定树的深度和含有 n 个结点的完全二叉树的深度相同。 为讨论方便,设 n=2h-1,且查找概率相等 则 在n>50时,可得近似结果 详细推导过程请参见课本或其他材料
静态查找方法比较 折半查找 顺序查找 小 (logn) 平均查找长度 大 (n) 有序表、无序表 有序表 逻辑结构 顺序存储结构,或线性链表 顺序存储结构 存储结构
让事实说话 快乐学习在线判题(Happy Learn Online Judge)
在线练习一:高考录取查询(8871~8873) Problem Description 高考招生结束了,你已经在第一时间获得了学校公布的已经被录取的全部N个准考证编号。 你的M个朋友,他们希望你能帮他们查一下,看看他们是否已被录取. 由于朋友数量太多,因此你不得不编写一个程序来完成这个任务。注意,这些朋友委托你查询的编号可能重复。
Input 输入数据的第一行为一个整数N(N不超过10000),表示有N个准考证编号. 这些准考证编号的范围在1到200000之间。然后是一个整数(M不超过10000),表示提供M个准考证编号,请你分别统计录取和未录取的数量。 Output分别输出录取和未录取的人数。 Sample Input 4 2 4 6 1 3 1 3 1 Sample Output 2 1
测试结果(高考录取查询) 请考虑哪些因素会影响测试效果? 系统的测试误差? 大规模数据的输入耗时? 其他因素?
在线练习二:小明的通讯录(8874~8876) Problem Description小明上中学了,为了方便和家里以及同学联系,爸爸给小明买了一台手机。该手机的存储容量可以扩充,因此,可存储的电话号码数量没有限制。 小明的手机有一个特殊的功能,对于打进或拨出的新号码,手机均会自动进行储存。给出小明的新手机在一个月内使用的通讯记录,请你回答此时此刻小明的手机中存储了多少个电话号码。
本问题应考虑哪些因素? 静态查找表能否达到目的? 查找表如何扩充,采用的方法和效率? • Input输入数据的第一行为一个整数N( N不超过10000),表示后面有N个电话号码。为方便处理,假设电话号码的长度是5位数,也就是范围在10000到99999之间。 • Output输出小明的手机中存储的电话号码总数。 • Sample Input 7 1000002 3000004 9000002 1000002 1000011 1234567 1000002 • Sample Output 5
结合前面刚学的知识以及对插入和删除操作的分析,这里我们给出小结:结合前面刚学的知识以及对插入和删除操作的分析,这里我们给出小结: 查找 插入 删除 (n) (1) (n) (1) (1) (1) (n) (1) (n) (n) (logn) (n) 无序顺序表 无序线性链表 有序顺序表 有序线性链表 注意,这里的插入和删除是在查找后进行的。
结论: 1)从查找性能看,最好情况能达(logn),此时要求逻辑结构有序; 2)从插入和删除的性能看,最好情况能达(1),此时要求存储结构是链表。 给我们的启发:若需要动态维护查找表,应该采用链式存储结构,且元素有序。
小结 引入了查找表 介绍了查找表的概念 分别讨论了顺序查找和折半查找在静态查找表中应用。 分析了顺序查找和折半查找的ASL。 利用在线测试总结和验证了顺序查找和折半查找的性能。
课后任务 复习总结静态查找表的知识 预习动态查找表的有关内容(重点预习平衡二叉树)