660 likes | 757 Views
第 11 章 查找. 查找. 静态查找表 动态查找表 哈希表. 查找. 查找表 :由同一类型的数据元素(或记录)构成的 集合 。 查找表的基本操作 1) 查询 某个“特定的”数据元素是否在表中 2) 检索 某个“特定的”数据元素的各种属性 3) 插入 一个数据元素 4) 删去 某个数据元素 静态查找表 :只作 查询 和 检索 操作的查找表。 动态查找表 :在查找过程中同时作 插入 或 删除 操作的查找表。. 查找. 记录 :由若干数据项构成的数据元素。 关键字 :能标识一个数据元素(或记录)的数据项。
E N D
查找 静态查找表 动态查找表 哈希表
查找 • 查找表:由同一类型的数据元素(或记录)构成的集合。 • 查找表的基本操作1) 查询某个“特定的”数据元素是否在表中 2) 检索某个“特定的”数据元素的各种属性 3) 插入一个数据元素 4) 删去某个数据元素 • 静态查找表:只作查询和检索操作的查找表。 • 动态查找表:在查找过程中同时作插入或删除操作的查找表。
查找 • 记录:由若干数据项构成的数据元素。 • 关键字:能标识一个数据元素(或记录)的数据项。 • 主关键字:能唯一地标识一个记录的关键字。 • 次关键字:用以识别若干记录的关键字。 学号 姓名 专业 年龄20030001 王洪 计算机 17 20030002 李文 计算机 18 20030003 谢军 计算机 18 20030004 张辉 信息工程 20 20030005 李文 信息工程 19
查找 • 查找:根据给定的某个值,在查找表中确定一个其关键字等于给定值的记录或数据元素,若表中存在这样的记录,则称查找成功,查找结果为该记录在查找表中的位置;否则称为查找不成功,查找结果为0或NULL。 学号 姓名 专业 年龄20030001 王洪 计算机 17 20030002 李文 计算机 18 20030003 谢军 计算机 18 20030004 张辉 信息工程 20 20030005 李文 信息工程 19
查找 • 查找方法评价:平均查找长度 • 平均查找长度ASL ( Average Search Length):为确定记录在表中的位置,需和给定值进行比较的关键字的个数的期望值叫查找算法的平均查找长度。 用于度量查找算法的效率 查找算法的基本操作:比较 平均查找长度ASL = PiCi Ci:查找第 i 个记录所需的比较次数 Pi:查找第 i 个记录的概率
静态查找表 • 静态查找 顺序表的查找:顺序查找 有序表的查找:折半查找 索引顺序表的查找:分块查找
静态查找表-顺序表的查找 • 查找表 用线性表表示L1=(45,61,12,3,37,24,90,53,98,78) • 用顺序表表示静态查找表 • 用线性链表表示静态查找表 • 查找方法:顺序查找
静态查找表-顺序表查找 ST.elem 0 1 2 3 4 5 6 7 8 9 10 m-1 45 61 12 3 37 24 90 53 98 78 • 顺序表类型定义 typedef struct { ElemType *elem; // 0号单元留空 int length; } SSTable; 与线性表顺序 存储结构比较一下 SSTable ST;
静态查找表-顺序表的查找 ST.elem 0 1 2 3 4 5 6 7 8 9 10 m-1 45 61 12 3 37 24 90 53 98 78 int Search_Seq ( SSTable ST, KeyType key ) { //在顺序表ST中顺序查找其关键字等于key的数据元素。若找到,则函数值为该元素在表中的位置,否则为0。 ST.elem[0].key = key; // “哨兵” for ( i=ST.length; !EQ(key, ST.elem[i].Key); --i ); return i; // 若表中不存在待查元素, i=0 } //Search_Seq 免去查找过程中每一步都要检测整个表是否查找完毕 key
静态查找表-顺序表的查找 例2:在下表中查找 key = 8 的结点。 i i i i i i i i i i key key ST.elem ST.elem 100 100 10 10 ……………… ……………… 0 0 8 7 1 1 3 3 0 0 1 1 2 2 n-3 n-3 n-2 n-2 n-1 n-1 n n 例1:在下表中查找 key = 8 的结点。 8 查找不成功,i = 0 8 查找成功,i = n-2
静态查找表-顺序表的查找 • 特点 无排序要求; 存储结构:顺序、链式; 平均查找长度ASLSS=(n+1)/2;
静态查找表-有序表查找 • 查找表:用有序表表示 • 查找方法:折半查找(二分查找) • 查找过程:先确定待查记录所在的范围(区间),然后逐步缩小范围直到找到或找不到该记录为止。 例:有原始查找表 {45,61,12,3,37,24,90,53,98,78} 为进行折半查找,需要先进行排序: L=(3,12,24,37,45,53,61,78,90,98)
静态查找表-有序表查找 low mid high high low Low mid 例:查找 Key=24的记录。 1 2 3 4 5 6 7 8 9 10 3 12 24 37 45 53 61 78 90 98 1 2 3 4 5 6 7 8 9 10 3 12 24 37 45 53 61 78 90 98 24<45 mid high 24>12 1 2 3 4 5 6 7 8 9 10 3 12 24 37 45 53 61 78 90 98
静态查找表-有序表查找 int Search_Bin ( SSTable ST, KeyType key ) { //在有序表ST中折半查找法查找其关键字等于key的数据元素。若找到,则返回该元素在表中的位置,否则为0。 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
静态查找表-有序表查找 • 该查找过程可用一个二叉树来描述 123456 78910 3,12,24,37,45,53,61,78,90,98 5 2 8 1 3 6 9 4 7 10 树中每个结点表示表中一个记录,结点中的值为该记录在表中的位置,通常称这个描述查找过程的二叉树为判定树。
静态查找表-有序表查找 此时ST.elem[mid].key=10 >key,因此令high=mid-1 此时ST.elem[mid].key=8 <key,因此令low=mid+1 此时ST.elem[mid].key=9 =key,查找成功,返回mid值 mid=2 mid=3 mid=4 key 4 8 9 10 11 13 19 elem 0 1 2 3 4 5 6 7 low=1 low=3 low=1 high=3 high=7 high=3 例1:在下表中查找 key = 9的结点。
静态查找表-有序表查找 此时ST.elem[mid].key=10 >key,因此令high=mid-1 此时ST.elem[mid].key=8 >key,因此令high=mid-1 mid=2 mid=1 mid=4 mid=1 key 4 8 9 10 11 13 19 elem 0 1 2 3 4 5 6 7 low=2 low=1 low=1 low=1 high=3 high=7 high=1 high=1 此时ST.elem[mid].key=4 <key,因此令low=mid+1 例2:在下表中查找 key = 5的结点。 此时low >high查找不成功
静态查找表-有序表查找 • 折半查找的特点 要求元素按关键字有序。 存储结构:顺序。 平均查找长度 ASLbs= log2(n+1)-1
静态查找表-索引顺序表的查找 索引表 查38 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 22 12 13 8 9 20 33 42 44 38 24 48 60 58 74 57 86 53 22 48 86 1 7 13 • 查找表的组织:分块索引,除表本身以外,尚需建立一个“索引表”。 • 查找方法:查找索引表;在数据块内顺序查找
静态查找表小结 • 查找方法比较 分块查找 顺序查找 折半查找 最大 最小 两者之间 ASL 表结构 有序表、无序表 有序表 分块有序表 顺序存储结构 线性链表 顺序存储结构 线性链表 存储结构 顺序存储结构
动态查找表 • 动态查找 二叉排序树:或者是一棵空树;或者是具有下列性质的二叉树: (1)若左子树不空,则左子树上所有结点的值均小于根结点的值; (2)若右子树不空,则右子树上所有结点的值均大于根结点的值; (3)根结点的左、右子树也分别为二叉排序树。
动态查找表 90 78 98 45 12 3 37 24 61 53 例:在二叉排序树中查找关键字为24的记录。
动态查找表 90 78 98 45 12 3 37 24 61 53 例:在二叉排序树中查找关键字为60的记录。
动态查找表 Lchild data rchild key … • 二叉排序树的存储 typedef struct BiTNode { TElemType data; struct BiTNode * lchild, * rchild; } BiTNode,*BTree; typedef struct { KeyType key; … } TElemType;
动态查找表 • 二叉排序树的查找——非递归算法 BiTree SearchBST ( BiTree T, KeyType key ) { /*二叉排序树用二叉链表存储。在根指针T所指二叉排序树中查找关键字等于key的记录,若查找成功,则返回该记录结点的指针,否则 返回空指针*/ p = T; while ( p && ! EQ ( key, p->data.key) ) { if (LT(key, T->data.key) ) p=p->lchild; else p=p->rchild; } return (p) ; } //SearchBST
动态查找表 • 二叉排序树的查找——递归算法 BiTree SearchBST( BiTree T, KeyType 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 )); } // SearchBST
动态查找表 二叉排序树的特点:是一种动态树表,树的结构通常不是一次生成的,而是在查找过程中,当树中不存在关键字等于给定值的结点时再进行插入。 新插入结点的特点:一定是一个新添加的叶子结点,并且是查找不成功时查找路径上访问的最后一个结点的左孩子或右孩子结点。 插入新结点的时机:当查找不成功时。
动态查找表 • 插入算法: (1)执行查找算法,找出被插入结点的双亲结点; (2)判断被插入结点是其双亲结点的左孩子结点还是右孩子结点,将被插入结点作为叶子结点插入; (3)若二叉树为空,则首先单独生成根结点。
动态查找表 Status SearchBST ( BiTree T, KeyType key, BiTree f, BiTree &p) { // 在根指针为T的二叉排序树中查找关键字等于key 的元素,若查找成功,则p指向该结点,并返回TRUE,否则指针p指向查找路径的最末结点,并返回FALSE 。f 指向T 的双亲,初始调用时为NULL。 if ( !T ) { p=f; return FALSE; } else if ( EQ(key,T->data.key) ) {p=T;return TRUE;} else if ( LT(key, T->data.key) ) return SearchBST ( T->lchild,key,T,p ); else return SearchBST ( T->rchild,key,T,p ); } //SearchBST
动态查找表 Status InsertBST ( BiTree &T, TElemType e) { /*当二叉排序树T中不存在关键字等于e.key的数据元素时,插入e并返回TURE,否则返回FALSE*/ if ( ! SearchBST(T, e.key, NULL, p) ) { //查找不成功 s = (BiTree) malloc(sizeof(BiTNode)); s->data=e; s->lchild=s->rchild=NULL; if (!p) T=s; // T为空,被插结点为根结点 else if ( LT(e.key, p->data.key) ) p->lchild=s; else p->rchild=s; return TRUE; } else return FALSE; //查找成功 } // InsertBST
动态查找表 key=280 f:NULL T f 122 122 T 250 250 99 99 300 300 110 110 122 122 122 key=280 f 250 250 250 99 99 99 key=280 p T f 300 300 300 110 110 110 T:NULL 280 例1:插入值为280的结点。
动态查找表 45 24 45 45 45 24 53 24 53 24 53 12 12 90 例2:在一棵空树中,查找如下的关键字序列 { 45,24,53,45,12,24,90 } 生成的二叉排序树如下: φ 45 对二叉排序树进行中序遍历可以得到有序序列
动态查找表 f F P p PR PL • 二叉树排序删除操作 删除原则:保持二叉排序树的特性。 设:二叉排序树上指向被删结点的指针为p,指向其双亲结点的指针为 f,且 p为 f的左孩子结点。
动态查找表 f F P p PR PL • 二叉排序树的删除操作 要删除二叉排序树中的p结点,分三种情况: • p为叶子结点,只需修改p 双亲f 的指针:f->lchild=NULL f->rchild=NULL • p只有左子树或右子树 • p左、右子树均非空
动态查找表 p 删除后 f P F • 二叉排序树的删除操作 要删除二叉排序树中的p结点,分三种情况: • p为叶子结点,只需修改p 双亲f 的指针:f->lchild=NULL f->rchild=NULL f F 删除前
动态查找表 S Q P PL (1) 中序遍历:PL P S Q 中序遍历:PL S Q S S S (2) Q PL PL Q P Q PL 中序遍历:Q S PL P 中序遍历:Q S PL • 二叉排序树的删除操作 • p只有左子树或右子树 • p只有左子树,用p的左孩子代替p (1)(2)
动态查找表 S (3) P Q PR 中序遍历:PR S Q 中序遍历:P PR S Q S S S (4) Q P Q PR PR Q PR 中序遍历:Q S PR 中序遍历:Q S P PR • 二叉排序树的删除操作 • p只有左子树或右子树 • p只有右子树,用p的右孩子代替p (3)(4)
动态查找表 F F P S PR C PR C CL Q CL Q S QL QL SL SL • 二叉排序树的删除操作 • p左、右子树均非空 • 沿p左子树的根C的右子树分支找到S,S的右子树为空,将S 的左子树成为S 的双亲Q 的右子树,用S取代p (5) 中序遍历:CL C … QL Q SL SP PR F (5) 中序遍历:CL C … QL Q SL S PR F
动态查找表 F F P C PR C PR CL CL • 二叉排序树的删除操作 • p左、右子树均非空 • 若C 无右子树,用C 取代p (6) 中序遍历:CL CPPR F 中序遍历:CL CPR F (6)
动态查找表 • 二叉排序树的删除操作 要删除二叉排序树中的p结点,分三种情况: • p为叶子结点,只需修改p 双亲f 的指针: f->lchild=NULL f->rchild=NULL • p只有左子树或右子树 • p只有左子树,用p的左孩子代替p (1)(2) • p只有右子树,用p的右孩子代替p (3)(4) • p左、右子树均非空 • 沿p 左子树的根C 的右子树分支找到S,S的右子树为空,将S 的左子树成为S 的双亲Q 的右子树,用S 取代p (5) • 若C 无右子树,用C 取代p (6)
动态查找表 例 80 80 60 120 50 120 删除60 删除10 删除7 删除50 60 150 150 110 110 55 70 80 55 70 53 10 55 120 8 8 53 5 25 150 5 25 110 5 25 53 70 13 8 13 7 13 6 7 6 6 • 二叉排序树的删除操作
动态查找表 98 90 78 45 12 3 37 24 61 53 3 12 24 37 45 单支树 53 61 78 90 98 • 性能分析 查找表:{3,12,24,37,45,53,61,78,90,98} ASL=(1+22+34+43)/10 =2.9 ASL=(1+2+3+4+5+6+7+8+9+10)/10=5.5
动态查找表 二叉排序树的特点之一(缺陷):没有对树的深度进行控制。 • 在构造二叉排序树的过程中进行“平衡化”处理,成为平衡二叉树(AVL树)。 • 平衡二叉树:左子树和右子树的深度之差的绝对值不超过计划。
动态查找表小结 • 二叉排序树的特点 含有n个结点的二叉排序树不唯一,与结点插入的顺序有直接关系。当查找失败后,在叶结点插入。 删除某个结点后,二叉排序树要重组。 没有对树的深度进行控制。 • 二叉排序树的适用范围 用于组织规模较小的、内存中可以容纳的数据。对于数据量较大必须存放在外存中的数据,则无法快速处理。
哈希表 • 问题:前面讨论的各种结构,记录在结构中的相对位置是随机的,和记录的关键字之间不存在确定关系,因此,在结构中查找记录时需要进行一系列和关键字的比较。 • 解决方法:理想情况是希望不经过任何比较,一次存取就能得到所查记录,因此必须在记录的存储位置和其关键字之间建立一个确定的对应关系f,使每个关键字和结构中的惟一的存储位置相对应。在查找时,只要根据该对应关系f可找到给定值K的像f(K)。若结构中存在关键字和K相等的记录,则一定在f(K)的存储位置上,所以不需要进行比较即可直接得到所查记录。 • 哈希表:对应关系f为哈希函数,根据上述思想建立的表为哈希表。
哈希表 • 散列法(哈希法) 基本思想:在记录的存储地址和它的关键字之间建立一个确定的对应关系;这样,不经过比较,一次存取就能得到所查元素的查找方法。即:通过简单计算直接得到数据的地址。 哈希(Hash)函数是一个映象,即:将关键字的集合映射到某个地址集合上,它的设置很灵活,只要这个地址集合的大小不超出允许范围即可。 哈希函数可写成:addr(ai)=H(ki) • ai是表中的一个元素 • addr(ai)是ai的存储地址 • ki是ai的关键字。
散列过程示例 0 H(k1) H(k5) U k1 k2 k5 S k4 H(k2)=H(k4) k3 H(k3) H(km-1)
哈希表 散列存储中经常会出现对于两个不同关键字xi,xj,却有H(xi)=H(xj),即对于不同的关键字具有相同的存放地址,这种现象称为冲突或碰撞。碰撞的两个(或多个)关键字称为同义词。 “负载因子”反映了散列表的装填程度,其定义为: 散列表中结点的数目 = 基本区域能容纳的结点数 • 当>1时冲突是不可避免的。 • 对于Hash方法,需要研究下面两个主要问题: • 选择一个计算简单,并且产生冲突的机会尽可能少的Hash函数 • 确定解决冲突的方法
哈希表-构造哈希函数 • 构造哈希函数的准则:使关键字经过哈希函数得到一个“随机的地址”,以便使一组关键字的哈希地址均匀地分布在逐个地址区间,从而减少冲突。 • 直接定址法:取关键字或关键字的某个线性函数值为哈希地址。如年龄。 • 数字分析法 • 平方取中法 • 折叠法 • 除留余数法