540 likes | 825 Views
第九章 查找. 静态查找表 动态查找表 二叉搜索树 AVL 树 哈希表 (Hash). 查找的基本概念. 备注. 概念. 查找表 查找操作针对的数据集,由同一类型的数据元素组成 关键字 (key) 数据元素中某个数据项的值,可唯一标识该数据元素 关键字和数据元素的类型说明,如. typedef float KeyType; // 实型 typedef int KeyType; // 整型 typedef char *KeyType; // 字符串型. typedef struct { KeyType key ; // 关键字域
E N D
第九章 查找 • 静态查找表 • 动态查找表 • 二叉搜索树 • AVL树 • 哈希表 (Hash)
查找的基本概念 备注
概念 • 查找表 • 查找操作针对的数据集,由同一类型的数据元素组成 • 关键字(key) • 数据元素中某个数据项的值,可唯一标识该数据元素 • 关键字和数据元素的类型说明,如 typedef float KeyType; //实型 typedef int KeyType; //整型 typedef char *KeyType; //字符串型 typedef struct { KeyType key; //关键字域 … //其它域 }ElemType;
概念 • 查找 • 在查找表中根据关键字找出特定的数据元素的操作 • 查找成功 • 关键字找到,返回对应数据元素在查询表中的位置 • 查找失败 • 关键字没有找到,返回空记录或空指针
概念 • 查找算法 • 取决于查找表的数据结构 • 静态查找表 • 查找表事先已确定 • 对查询表除了创建和销毁操作外,只有查找或遍历操作 • 动态查找表 • 查找表本身在查找过程中动态生成,即若没有找到,则插入该元素 • 对查询表除了创建、销毁、查找、遍历操作外,还能进行插入和删除操作
对两个关键字的比较约定为如下的宏定义: 对数值型关键字#define EQ(a,b) ((a)==(b))#define LT(a,b) ((a)<(b))#define LQ(a,b) ((a)<=(b)) 对字符串型关键字#define EQ(a,b) (!strcmp((a),(b)))#define LT(a,b) (strcmp((a),(b))<0)#define LQ(a,b) (strcmp((a),(b))<=0)
9.1 静态表查找 • 顺序表的查找 • 有序表的查找 • 索引顺序表的查找
ADT StaticSearchTable{ 数据对象D:D是具有相同特性的数据元素的集合。各个数据元素均含有类型相同,可唯一标识数据元素的关键字。 数据关系R:数据元素同属一个集合。 基本操作P: 静态查找表的类型定义:
Create(&ST,n);操作结果:构造一个含n个数据元素的静态查找表ST。Create(&ST,n);操作结果:构造一个含n个数据元素的静态查找表ST。 Destroy(&ST);初始条件:静态查找表ST存在。操作结果:销毁表ST。 Search(ST,key);初始条件:静态查找表ST存在,key为和关键字类型相同的给定值。 操作结果:若ST中在在其关键字等于key的数据元素,则函数值为该元素的值或在表中的位置,否则为“空”。 Traverse(ST,Visit());初始条件:静态查找表ST存在,Visit是对元素操作的应用函数。操作结果:按某种次序对ST的每个元素调用函数visit()一次且仅一次。一旦visit()失败,则操作失败。 }ADT StaticSearchTable
9.1.1顺序表的查找 顺序查找:从表中最后一个记录开始,逐个进行记录的关键字和给定值的比较,若某个记录的关键字和给定值比较相等,则查找成功,找到所查记录;反之,查找不成功。
静态查找表的顺序存储结构 typedef struct { ElemType *elem; int length; }SSTable; 查找算法 int Search_Seq(SSTable ST,KeyType key){ ST.elme[0].key=key; for(i=ST.length; !EQ(ST.elem[i].key,key); --i); return i; }
查找操作的性能分析: 查找算法中的基本操作是将记录的关键字和给定值进行比较,通常以“其关键字和给定值进行过比较的记录个数的平均值”作为衡量依据。 平均查找长度 Average Search Length: 为确定记录在查找表中的位置,需用和给定值进行比较的关键字个数的期望值称为查找算法在查找成功时的平均查找长度。
其中:Pi为查找表中第i个记录的概率,且 ; Ci为找到表中其关键字与给定值相等的第i个记录时,和给定值已进行过比较的关键字个数。 等概率条件下有: 假设查找成功与不成功的概率相同:
9.1.2静态查找表(二)有序表的查找 折半查找的查找过程 以有序表表示静态查找表时,Search函数可用折半查找来实现。 先确定待查记录所在的范围(区间),然后逐步缩小范围直到找到或找不到该记录为止。
折半查找的查找实现 int Search_Bin(SSTable ST,KeyType key){ low=1;high=ST.length; while(low<=high){ mid=(low+high)/2; if EQ(key,ST.elem[mid].key) return mid; else if LT(key,ST.elem[mid].key) high=mid -1; else low=mid +1 ; } return 0; }//Search_Bin;
折半查找的性能分析 1 折半查找在查找成功时和给定值进行比较的关键字个数至多为 2 折半查找在查找不成功时和给定值进行比较的关键字个数至多为
9.1.3索引顺序表查找 • 又称分块查找 • 是顺序查找的改进 • 除了查找表以外,还需一个索引表结构
22 12 13 8 9 20 33 42 44 索引表 38 24 22 1 44 块中最大key 48 7 38 24 块第一个元素在查找表中的位置 86 13 48 60 58 (有序) 74 49 86 53 查找表 (只描述了关键字部分) (块间有序,块内无序)
索引顺序表查找 • 分块查找算法 • 首先,根据关键字值在索引表中查找(顺序或折半查找)以确定块的范围 • 然后,在块中根据关键字值做顺序查找 • 性能分析 • 若索引表查找采用顺序查找,则平均查找长度为(n/s+s)/2 +1;若索引表查找采用折半查找,则平均查找长度为log2(n/s+1)+s/2;其中n为查找表表长,s为每一块中记录个数 • 查找效率高,但需建立索引表
9.2 动态查找表 • 动态查找表的特点 • 表结构本身是在查找过程中动态生成的,即对于给定值key,若表中存在其关键字等于key的记录,则查找成功返回,否则插入关键字等于key的记录 • 动态查找表允许的操作 • 创建表、销毁表、关键字查找、记录插入、记录删除、记录遍历等
动态查找表 ADT DymanicSearchTable{ 数据对象D:D是具有相同特性的数据元素的集合。各个数据元素均含有类型相同,可唯一标识数据元素的关键字。 数据关系R:数据元素同属一个集合。 基本操作P: InitDSTable(&DT); DestroyDSTable(&DT); SearchDSTable(DT,key); InsertDSTable(&DT,e); DeleteDSTable(&DT,key); TraverseDSTable(DT,Visit()); }ADT DynamicSearchTable
动态查找表 • 二叉排序树 (*) • 平衡二叉树
9.2.1 二叉排序树及其查找过程 二叉排序树或者是一棵空树;或者是具有下列性质的二叉树: 1、若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 2、若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 3、它的左、右子树了分别为二叉排序树。
45 CAO 12 53 ZHAO 3 37 100 DING CHEN WANG 24 61 MA XIA 90 DU LI 78 二叉排序树示例
lchild data rchild 二叉排序树 • 二叉排序树的存储结构 • 通常采用二叉链表结构
二叉排序树 • 二叉排序树的查找算法一 (不考虑查找失败后插入记录) BiTree SearchBST(BiTree T, KeyType key) { //在根指针T所指二叉排序树中递归地查找某关键字等于key的数据元素, //若查找成功则返回指向该数据元素的指针,否则返回空指针 if ((!T) || EQ(key, T->data.key)) return (T); else if (LT(key, T->data.key)) return (SearchBST(T->lchild, key)); else return (SearchBST(T->rchild, key)); }
45 45 45 45 45 24 24 53 24 53 24 53 12 90 12 a b c d e 假设查找的关键序列为{45,24,53,45,12,24,90}, 则生成二叉排序树的过程如下图所示:
二叉排序树 • 二叉排序树的特点 • 中序遍历二叉排序树可得到一个关键字的有序序列(即:可以通过输入一个无序序列,而通过构造二叉排序树后进行遍历的方法实现排序) • 二叉排序树的插入过程不需要移动其他记录 • 具有相同n个结点的二叉排序树会因为构造的时候关键字插入的顺序不同而不同(下页说明) • 二叉排序树的查找算法具有折半查找的特性,同时又采用了链式存储结构,因此是动态查找表的适宜表示
14 27 16 16 16 32 14 27 27 32 14 32 如:查找的关键序列分别为{27,32,16,14}、{16,14,27,32}和{14,16,27, 32}, 生成二叉排序树分别如下:
二叉排序树的查找分析 • 最差情况 (n+1)/2 • 最好情况 log2n • 平均性能
思考:如何找到某子树上最大值结点,如何找到最小值结点?思考:如何找到某子树上最大值结点,如何找到最小值结点?
在二叉排序树中删除一个节点 x 如何找到 w ?
平衡二叉树 AVL树 高度平衡的二叉搜索树 一棵AVL树或者是空树,或者是具有下列性质的二叉搜索树:它的左子树和右子树都是AVL树,且左子树和右子树的高度之差的绝对值不超过1。 高度不平衡的二叉搜索树 高度平衡的二叉搜索树
结点的平衡因子balance (balance factor) • 每个结点附加一个数字,给出该结点右子树的高度减去左子树的高度所得的高度差。这个数字即为结点的平衡因子balance。 • 根据AVL树的定义,任一结点的平衡因子只能取 -1,0和 1。 • 如果一个结点的平衡因子的绝对值大于1,则这棵二叉搜索树就失去了平衡,不再是AVL树。 • 如果一棵二叉搜索树是高度平衡的,它就成为 AVL树。如果它有 n 个结点,其高度可保持在O(log2n),平均搜索长度也可保持在O(log2n)。
AVL树的高度 • 设在新结点插入前AVL树的高度为 h,结点个数为 n,则插入一个新结点的时间是O(h)。对于AVL树来说,h 多大? • 设 Nh 是高度为 h 的AVL树的最小结点数。根的一棵子树的高度为 h-1,另一棵子树的高度为 h-2,这两棵子树也是高度平衡的。因此有 • N-1 = 0 (空树) • N0 = 1 (仅有根结点) • Nh= Nh-1 + Nh-2 +1 , h > 0 • 可以证明,对于 h 0,有 Nh = Fh+3 -1成立。
平衡化旋转 • 如果在一棵平衡的二叉搜索树中插入一个新结点,造成了不平衡。此时必须调整树的结构,使之平衡化。 • 平衡化旋转有两类: • 单旋转 (左旋和右旋) • 双旋转 (左平衡和右平衡) • 每插入一个新结点时,AVL树中相关结点的平衡状态会发生改变。因此,在插入一个新结点后,需要从插入位置沿通向根的路径回溯,检查各结点的平衡因子(左、右子树的高度差)。 • 如果在某一结点发现高度不平衡,停止回溯。
从发生不平衡的结点起,沿刚才回溯的路径取直接下两层的结点。从发生不平衡的结点起,沿刚才回溯的路径取直接下两层的结点。 • 如果这三个结点处于一条直线上,则采用单旋转进行平衡化。单旋转可按其方向分为左单旋转和右单旋转,其中一个是另一个的镜像,其方向与不平衡的形状相关。 • 如果这三个结点处于一条折线上,则采用双旋转进行平衡化。双旋转分为先左后右和先右后左两类。 右单旋转 左单旋转 右左双旋转 左右双旋转
0 左单旋转 (RotateLeft ) A C A +1 +2 0 +1 0 B E B C C A h h D D D h + 1 E E B • 如果在子树E中插入一个新结点,该子树高度增1导致结点A的平衡因子变成+2,出现不平衡。 • 沿插入路径检查三个结点A、C和E。它们处于一条方向为“\”的直线上,需要做左单旋转。 • 以结点C为旋转轴,让结点A反时针旋转。 h h h h + 1 h h (a) (b) (c)
0 右单旋转 (RotateRight ) -1 A -2 A B C C 0 -1 0 B D B A E h E h E C D D h + 1 • 在左子树D上插入新结点使其高度增1,导致结点A的平衡因子增到 -2,造成了不平衡。 • 为使树恢复平衡,从A沿插入路径连续取3个结点A、B和D,它们处于一条方向为“/”的直线上,需要做右单旋转。 • 以结点B为旋转轴,将结点A顺时针旋转。 h h h + 1 h h h (a) (b) (c)
先左后右双旋转 (RotationLeftRight) • 在子树F或G中插入新结点,该子树的高度增1。结点A的平衡因子变为 -2,发生了不平衡。 • 从结点A起沿插入路径选取3个结点A、B和E,它们位于一条形如“”的折线上,因此需要进行先左后右的双旋转。 • 首先以结点E为旋转轴,将结点B反时针旋转,以E代替原来B的位置,做左单旋转。 • 再以结点E为旋转轴,将结点A顺时针旋转,做右单旋转。使之平衡化。
-2 -1 左单 旋转 +1 0 插入 0 -1 右单 旋转 0 0 +1
先右后左双旋转 (RotationRightLeft) • 右左双旋转是左右双旋转的镜像。 • 在子树F或G中插入新结点,该子树高度增1。结点A的平衡因子变为2,发生了不平衡。 • 从结点A起沿插入路径选取3个结点A、C和D,它们位于一条形如“”的折线上,需要进行先右后左的双旋转。 • 首先做右单旋转:以结点D为旋转轴,将结点C顺时针旋转,以D代替原来C的位置。 • 再做左单旋转:以结点D为旋转轴,将结点A反时针旋转,恢复树的平衡。
+1 +2 0 -1 0 +1 右旋转 插入 0 左单 旋转 -1 0