Download presentation
Presentation is loading. Please wait.
1
本章重点难点 重点:顺序查找、二分查找、二叉排序树查找以及散列表查找的基本思想和算法实现。
难点:二叉排序树的删除算法和平衡二叉树的构造算法。
2
第9章 查 找 预备知识 9.1 静态查找表 9.2 动态查找表 9.3 哈希表
3
预备知识 1、有关概念 2、类型定义 3、宏定义
4
1、有关概念 (1)、查找表: 是由同一类型的数据元素(或记录)构成的集合。 (2)、静态查找表: 仅作查询和检索操作的查找表。
(3)、动态查找表: 在查找时包含插入、删除或修改。
5
1、有关概念 (4)、主关键字 可以识别唯一的一个记录的数据项(字段)。 (5)、次关键字: 关联若干项记录的数据项(字段)。
(6)、查找: 根据给定的某个值,在查找表中确定一个其关键字 等于给定值的数据元素或(记录)。
6
1、有关概念 (7)、查找成功: 查找表中存在满足条件的记录。 (8)、查找不成功: 查找表中不存在满足条件的记录。
7
预备知识 1、有关概念 2、类型定义 3、宏定义
8
2、类型定义 typedef float Keytype; //Keytype定义为浮点数据类型 typedef int Keytype;
typedef char *Keytype; //Keytype定义为字符指针数据类型
9
2、类型定义 数据元素类型定义为: typedef struct{ Keytype key;//关键字 … }SElemType;
10
预备知识 1、有关概念 2、类型定义 3、宏定义
11
3、宏定义 //数值型关键字的比较 #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)
12
第9章 查 找 预备知识 9.1 静态查找表 9.2 动态查找表 9.3 哈希表
13
9.1 静态查找表 9.1.1 顺序表的查找 9.1.2 有序表的查找 *9.1.3 静态树表的查找 //自学 9.1.4 索引顺序表的查找
14
9.1 静态查找表 见下页 静态查找表的抽象数据类型 ADT StaticSearchTable {
数据元素同属一个集合。 基本操作 P: 见下页 静态查找表的抽象数据类型 } ADT StaticSearchTable
15
9.1 静态查找表 静态查找表的4个基本操作 Create(&ST, n); //建立静态查找表
Destroy(&ST); //销毁静态查找表 Search(ST, key); //按关键字字key查找 Traverse(ST, Visit()); //遍历查找表
16
9.1.1 顺序查找表 1、顺序查找表的存储结构 2、顺序查找表的查找思想 3、顺序查找表示例演示 4、顺序查找表的算法与实现
5、顺序查找表的算法分析
17
9.1.1 顺序查找表 1、顺序查找表存储结构 typedef struct { // 数据元素存储空间基址,建表时
// 按实际长度分配,0号单元留空 int length; // 表的长度 } SSTable; ElemType *elem;
18
9.1.1 顺序查找表 2、顺序查找表的思想 (1)从查找表的第一个元素向后(或从最后一个元素向前),比较当前位置数据元素的关键字与查找关键字; (2)若相等,输出当前位置,查找成功,若不相等,走向下一个位置; (3)循环执行(1)、(2)步,直到查找成功或超出范围,表示查找失败。
19
9.1.1 顺序查找表 3、顺序查找表示例演示 查找55,从后向前 监视哨 表示没找到,返回值为0 67 78 76 45 33 99 88
20
9.1.1 顺序查找表 3、顺序查找表示例演示 查找33,从后向前 监视哨 查找成功,返回当前位置5 67 78 76 45 33 99
88 监视哨 55 67 78 76 45 33 99 88 查找成功,返回当前位置5
21
9.1.1 顺序查找表 4、顺序查找表的算法与实现 int Search_Seq(SSTable ST, KeyType key) {
// 为该元素在表中的位置,否则为0。 ST.elem[0].key = key; // “哨兵” for (i=ST.length; ST.elem[i].key!=key; --i); // 从后往前找 return i; // 找不到时,i为0 } // Search_Seq
22
9.1.1 顺序查找表 5、顺序查找表算法分析 给定值进行比较的关键字个数的期望值: n 为表长 Pi 为查找表中第i个记录的概率
Ci为找到记录时,关键字的比较次数
23
9.1.1 顺序查找表 5、顺序查找表算法分析 什么情况下取最小值? 在等概率查找的情况下, 顺序表查找的平均查找长度为:
思考题:在概率不等的情况下,ASL在 什么情况下取最小值?
24
9.1.1 顺序查找表 5、顺序查找表算法分析 答案:在不等概率查找的情况下,ASL 在 Pn≥Pn-1≥···≥P2≥P1 时取最小值。
25
9.1.2、有序表的查找 折半查找的前提要求 折半查找的思想 折半查找示例演示 折半查找算法与实现 折半查找算法分析
26
9.1.2、有序表的查找 折半查找的前提要求 想一想:英文字典的特点及英语单词的查找过程。 英文字典是有序的,英语单词的查找用的是折半查找。
9.1.2、有序表的查找 折半查找的前提要求 想一想:英文字典的特点及英语单词的查找过程。 英文字典是有序的,英语单词的查找用的是折半查找。 折半查找的前提要求是顺序存储并且有序。
27
9.1.2、有序表的查找 2. 折半查找表的思想 (1)、将要查找关键字与查找表的中间的元素进行比较,若相等,返回当前位值;
9.1.2、有序表的查找 2. 折半查找表的思想 (1)、将要查找关键字与查找表的中间的元素进行比较,若相等,返回当前位值; (2)、若查找关键字比当前位置关键字大,向前递归,否则向后递归。
28
9.1.2、有序表的查找 3. 折半查找表示例演示 查找21 mid 位置 0 1 2 3 4 5 6 7 8 9 10
9.1.2、有序表的查找 3. 折半查找表示例演示 查找21 位置 关键字 low mid= (low+high)/2 high mid high=mid-1 low=mid+1 mid
29
9.1.2、有序表的查找 4. 折半查找表算法与实现 int Search_Bin ( SSTable ST, KeyType key ) { low = 1; high = ST.length; // 置区间初值 while (low <= high) { ……………… } return 0; // 顺序表中不存在待查元素 } // Search_Bin
30
9.1.2、有序表的查找 4. 折半查找表算法与实现 while (low <= high) {
9.1.2、有序表的查找 4. 折半查找表算法与实现 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; // 继续在后半区间进行查找 }
31
9.1.2、有序表的查找 5. 折半查找表算法分析 用判定树描述折半查找的过程。 设n=2h-1 ( h=log2(n+1) ) 5 平均查找长度 2 8 3 6 9 1 4 7 10
32
9.1.4、索引顺序表的查找 索引顺序表的前提要求 索引顺序表的思想 索引顺序表表示例演示 索引顺序表算法与实现 索引顺序表算法分析
33
9.1.4、索引顺序表的查找 1. 索引顺序表的前提要求 (1)、查找表要求顺序存储
(2)、查找表分成n块,当i>j时,第i块中的最小元素大于第j块中的最大元素。
34
9.1.4、索引顺序表的查找 2. 索引顺序表的查找思想 (1)、首先确定所要查找关键字在哪一块中。
2. 索引顺序表的查找思想 (1)、首先确定所要查找关键字在哪一块中。 (2)、在所确定的块中用顺序查找查找关键字。
35
9.1.4、索引顺序表的查找 3. 索引顺序表的示例演示 索引表 该块最大关键字 该块起始地址 0 1 2 3 4 5 6 7 8 9 10
22 48 86 4 7 22 12 9 20 33 42 48 49 60 86 53 线性表
36
9.1.4、索引顺序表的查找 4. 索引顺序表的算法与实现 索引表的存储结构 typedef struct {keytype key;
int addr; }indextype; {indextype index[maxblock]; int block; }INtable; INtable IX;
37
9.1.4、索引顺序表的查找 4. 索引顺序表的算法与实现
int SEARCH(SSTable ST,INtable IX,KeyType key ) { int i=0,s,e; //s记录在查找表中的开始位置 //e记录在查找表中的结束位置 {在索引表中查找,确定s和e的值} {根据s和e的值在线性表中查找} return -1 }
38
9.1.4、索引顺序表的查找 4. 索引顺序表的算法与实现
while ((key>IX.index[i].key)&&(i<=IX.block)) i++; if (i<=IX.block) {s=IX.index[i].addr; if (i==IX.block) e=ST.length; else e=IX.index[i+1].addr-1; while (key!=ST.elem[s].key&&(s<=e) s=s+1; if (s<=e) return s; } return -1
39
9.1.4、索引顺序表的查找 5. 索引顺序表的算法分析 思考题:如果索引表长度为b,每块平均长度为L 平均查找长度是多少?
思考题:长度为n的线性表,分成多少块平均查找次 数最少? 答案:
40
第9章 查 找 预备知识 9.1 静态查找表 9.2 动态查找表 9.3 哈希表
41
9.2 动态查找表 9.2.1 二叉排序树和平衡二叉树 9.2.2 B树和B+树 **9.2.3 键树 //自学
42
9.2 动态查找表 抽象数据类型动态查找表的定义如下: ADT DynamicSearchTable {
9.2 动态查找表 抽象数据类型动态查找表的定义如下: ADT DynamicSearchTable { D是具有相同特性的数据元素的集合,每个数据元素含有类型相同的关键字,可唯一标识数据元素。 数据对象D: 数据关系R: 数据元素同属一个集合。 见下页: 基本操作P: }ADT DynamicSearchTable
43
9.2 动态查找表 动态查找表的6个基本操作 InitDSTable(&DT) DestroyDSTable(&DT)
9.2 动态查找表 动态查找表的6个基本操作 InitDSTable(&DT) DestroyDSTable(&DT) SearchDSTable(DT, key); InsertDSTable(&DT, e); DeleteDSTable(&T, key); TraverseDSTable(DT, Visit());
44
9.2.1、二叉排序树和平衡二叉树 二叉排序树 平衡二叉树
45
一. 二叉排序树 二叉排序树的定义 二叉排序树的存储结构 二叉排序树的查找 二叉排序树的结点插入 二叉排序树的结点删除
46
1. 二叉排序树的定义 定义:二叉排序树(Binary Sort Tree)或者是一棵空树; 或者是具有下列性质的二叉树:
若它的左子树不空,则左子树上所有的结点的关键字的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有的结点的关键字的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
47
2. 二叉排序树的存储结构 通常采用二叉链表作为二叉排序树的存储结构。 typedef struct BiTNode { // 结点结构
struct BiTNode *lchild, *rchild; // 左右孩子指针 } BiTNode, *BiTree; TElemType data;
48
3. 二叉排序树的查找 二叉排序树的查找思想 二叉排序树的查找示例演示 二叉排序树的查找算法 二叉排序树的查找算法分析
49
① 二叉排序树的查找思想 a.当二叉排序树不空时,先将给定值和根结点的关键字比较,若相等,则查找成功; 否则:
b.若给定值小于根结点的关键字,则在左子树上继续进行查找; c.若给定值大于根结点的关键字,则在右子树上继续进行查找; d.直到找到或查到空结点时为止。
50
② 二叉排序树的查找示例演示 例如:查24, 45 45 12 12 53 37 3 37 100 24 24 61 90 78
51
② 二叉排序树的查找示例演示 45 53 100 61 90 NULL 例如:查24, 45 12 53 3 37 24 61 90 78
52
③ 二叉排序树的查找算法 BiTree SearchBST(BiTree T,keytype k) {BiTree p=T;
while(p!=NULL) {if (p->data.key==k) return p; if (k<p->data.key) p=p->lchild else p=p->rchild } return NULL; ***
53
④ 二叉排序树的查找算法分析 12 45 24 24 53 37 12 37 94 45 53 树1平均查找长度 O(log2n)
树2平均查找长度 O(n) 94
54
④ 二叉排序树的查找算法分析 a.二叉排序树的平均查找长度最差情况与顺序表相同(关键字有序时),为O(n);
b.最好情况与折半查找相同,是O(log2n)数量级的; c.二叉排序树的平均查找长度仍然是O(log2n)。 思考题:若按中序对二叉查找树进行遍历,遍历 序列有什么特点? 答案:按关键字升序排列。
55
4. 二叉排序树的结点插入 二叉排序树的插入算法思想 二叉排序树的插入过程演示 二叉排序树的插入算法 二叉排序树的插入算法分析
56
二叉排序树的插入算法思想 若大于根结点,插入右子树。 a.若二叉树为空: 则待插入结点*s作为根结点; b.当二叉排序树非空时:
将待插结点关键字与根结点进行比较,若相等,则说明树中已有此结点,无须插入; 若小于根结点,插入左子树; 若大于根结点,插入右子树。
57
② 二叉排序树的插入过程演示 在如下二叉排序树中插入25过程演示 45 45 12 12 53 37 3 37 100 24 24 61
58
③ 二叉排序树的插入算法 Status InsertBST(BiTree &T, ElemType e) { BiTree p,s;
if (!SearchBST(T, e.key, NULL, p)) // 查找不成功 { s = (BiTree)malloc(sizeof(BiTNode)); s->data = e; s->lchild = s->rchild = NULL; if (!p) T = s; // 插入 s 为新的根结点 else if (LT(e.key, p->data.key)) p->lchild=s; // 插入s为左孩子 else p->rchild = s; // 插入 s 为右孩子 return TRUE; } else return FALSE; // 树中已有关键字相同的结点,不再插入 } // Insert BST
59
④ 二叉排序树的插入算法分析 a.二叉排序树的插入算法的时间复杂性与查找算法的时间复杂性相同;
b.最好情况是O(log2n);最坏情况是O(n);平均情况是O(log2n)。 思考题:如何生成二叉排序树? 答案:从空树开始循环调用插入算法。
60
5. 二叉排序树的结点删除 二叉排序树的结点删除定义 二叉排序树的结点删除方法 二叉排序树的结点删除算法 二叉排序树的结点删除算法分析
61
① 二叉排序树的结点删除定义 在二元查找树上的删除一个结点,要求删除结点后,仍保持二叉排序树的结构特点不变。
62
②二叉排序树的结点删除方法 设被删结点为p,其双亲结点为f,则p可能有以下4种情况: a、p为叶结点 b、p只有左子树 c、p只有右子树
d、p左右子树都有
63
②二叉排序树的结点删除方法 f p 45 a、p为叶结点,如右图所示: 12 53 3 37 51 100 删除方法:
释放结点p,修改其父结点f的相应指针。 24 61 p 25
64
②二叉排序树的结点删除方法 45 45 12 53 12 53 3 51 100 3 37 51 100 24 p 25 24 61 61 25 b、p只有左子树, 如上图 所示: 删除方法:释放结点p,p的左子树顶替p点的位置。
65
②二叉排序树的结点删除方法 45 45 12 53 12 100 3 37 100 3 37 61 24 61 24 25 25 c、p只有右子树, 如上图 所示: 删除方法:释放结点p,p的右子树顶替p点的位置。
66
②二叉排序树的结点删除方法 删除方法: 45 把左子树作为右子树中最小结点的左子树。 12 53 或者把右子树作为左子树中最大结点的右子树。
37 51 100 24 61 缺点: 增加了树的高度。 25 d、p既有左子树,也有右子树, 如上图 所示:
67
②二叉排序树的结点删除方法 45 改进删除方法: 12 53 找一个结点顶替它的位置。 3 37 51 100
能够顶替它的结点是左子树中最大的,右子树中最小的。 我们用左子树最大的结点来顶替。 24 61 25 d、p既有左子树,也有右子树, 如上图 所示:
68
③二叉排序树的结点删除算法 Status DeleteBST(BiTree &T, KeyType key) {
if (!T) return FALSE; else { if (EQ(key, T->data.key)) return Delete(T); else if (LT(key, T->data.key)) return DeleteBST(T->lchild, key); else return DeleteBST(T->rchild, key); } } // DeleteBST
69
③二叉排序树的结点删除算法 Status Delete(BiTree &p) { BiTree q, s;
if (!p->rchild) {q = p; p = p->lchild; free(q);} else if (!p->lchild) {q = p; p = p->rchild; free(q);} else {q = p; s = p->lchild;
70
③二叉排序树的结点删除算法 while (s->rchild) {q = s; s = s->rchild; }
p->data = s->data; if (q != p) q->rchild = s->lchild; else q->lchild = s->lchild; free(s); } return TRUE; } // Delete
71
④ 二叉排序树的删除算法分析 a.二叉排序树的删除算法的时间复杂性与查找算法的时间复杂性相同;
b.最好情况是O(log2n);最坏情况是O(n);平均情况是O(log2n)。
72
9.2.1、二叉排序树和平衡二叉树 二叉排序树 平衡二叉树
73
二. 平衡二叉树 平衡二叉树的定义 平衡二叉树的构造示例演示 平衡二叉树的构造思想 平衡二叉树的构造算法
74
1. 平衡二叉树的定义 平衡二叉树又称AVL树 一棵AVL树或者是空树,或者是具有下列性质的二叉排序树:它的左子树和右子树都是AVL树,且左子树和右子树的高度之差的绝对值不超过1。
75
1. 平衡二叉树的定义 45 45 24 53 24 53 12 37 94 12 37 94 96 平衡二叉树 非平衡二叉树
76
1. 平衡二叉树的定义 平衡因子(BF--Balance Factor): 任一结点的左子树的高度减去右子树的高度所得的高度差称为该结点的平衡因子BF。 根据AVL树的定义,任一结点的平衡因子只能取 -1,0 和 1。 一棵平衡二叉排序树如果有n个结点,其高度可保持在O(log2n),平均查找长度也可保持在O(log2n)。
77
2.平衡二叉树的构造示例演示 例:给定序列25、27、30、12、11、18、14、20、15、22,构造其AVL树。 -1 -2 25
25 25 -1 25 RR旋转 27 27 1 30 27 27 1 25 30 25 30 12
78
2.平衡二叉树的构造示例演示 例:给定序列25、27、30、12、11、18、14、20、15、22,求其AVL树 LL旋转 1 2 1
27 1 2 25 30 25 30 30 12 1 12 12 11 25 11
79
2.平衡二叉树的构造示例演示 例:给定序列25、27、30、12、11、18、14、20、15、22,求其AVL树 LR旋转 先左旋后右旋
2 -1 30 30 25 12 25 -1 1 12 27 11 25 12 30 11 18 18 11 18
80
2.平衡二叉树的构造示例演示 例:给定序列25、27、30、12、11、18、14、20、15、22,求其AVL树 1 25 25 -1
1 25 25 -1 -1 -1 27 27 12 12 30 30 11 18 11 18 14 20
81
2.平衡二叉树的构造示例演示 例:给定序列25、27、30、12、11、18、14、20、15、22,求其AVL树 2 RL旋转 2
先右旋后左旋 25 25 -1 -1 -2 -2 27 12 27 12 1 1 30 11 30 18 11 14 -1 14 20 18 15 15 20
82
2.平衡二叉树的构造示例演示 例:给定序列25、27、30、12、11、18、14、20、15、22,求其AVL树 1 RL旋转
先右旋后左旋 25 -1 27 14 1 12 30 18 11 15 20
83
3.平衡二叉树的构造思想 在构造过程中,每当插入一个新结点时,首先检查是否因插入而破坏了树的平衡性。
若是, 则找出其中最小不平衡子树,在保持二叉排序树特性的前提下, 调整最小不平衡子树中各结点之间的连接关系,以达到新的平衡。 最小不平衡子树:以离插入结点最近,且平衡因子绝对值大于 1 的结点作为根的子树。 假设二叉排序树的最小不平衡子树的根结点为 A ,则有四种形态需要调整。
84
4.平衡二叉树的构造算法 A A B C C B B D A D E h D E h E C h + 1 h h h + 1 h h h
① LL型—右旋 1 A 2 A B C 1 C B B D A D E h D E h E C h + 1 h h h + 1 h h h (a) (b) (c)
85
4.平衡二叉树的构造算法 A C B D E h h + 1 h B D A E C h + 1 h h ① LL型—右旋 2
void R_Rotate(BSTree &p) {BSTree lc; lc = p->lchild; p->lchild=lc->rchild; lc->rchild=p; p = lc; } 1 C B D E h h + 1 h B D A E C h + 1 h h
86
4.平衡二叉树的构造算法 A A C B C B C A h D h D D E E h + 1 B h h h h + 1 h h
② RR型— 左旋 -1 A -2 A C -1 B C B C A h D h D D E E h + 1 B h h h h + 1 h h (a) (b) (c)
87
4.平衡二叉树的构造算法 A B C D E C A D h + 1 B h h ② RR型— 左旋 -2 -1
void L_Rotate(BSTree &p) {BSTree rc; rc = p->rchild; p->rchild=rc->lchild; rc->lchild = p; p = rc; } h D E h h + 1 C A D h + 1 B h h
88
4.平衡二叉树的构造算法 #define EH 0 #define LH 1 #define RH -1
Status InsertAVL(BSTree &T,ElemType e, Boolean &taller) { if(!T) { T =(BSTree)malloc(sizeof(BSTNode)); T->data = e; T->lchild = T->rchild = NULL; T->bf = EH; taller = TRUE; }
89
4.平衡二叉树的构造算法 else { if(EQ(e.key,T->data.key)) {taller=FALSE;return 0;} // 则不再插入 if (LT(e.key,T->data.key)){ if (InsertAVL(T->lchild,e,taller)==0) return 0;
90
4.平衡二叉树的构造算法 Status InsertAVL(BSTree &T,ElemType e,
Boolean &taller) { …………… if (taller) switch (T->bf) { case LH: LeftBalance(T);taller = FALSE;break; case EH: T->bf=LH;taller=TRUE;break; case RH: T->bf=EH;taller=FALSE;break; } // switch (T->bf) } // if
91
4.平衡二叉树的构造算法 …………… else {if(InsertAVL(T->rchild,e,taller)==0) return 0; if (taller) switch (T->bf) {case LH: T->bf = EH;taller=FALSE;break; case EH: T->bf=RH;taller=TRUE;break; case RH: RightBalance(T); taller=FALSE;break; } // switch (T->bf) } // else } // else return 1; } //InsertAVL ****
92
9.2.2、B-树和B+树 B-树 B+树
93
一、B-树 1. B-树的定义 2. B-树的查找算法 3. B-树的结点插入过程演示 4. B-树的结点插入算法
94
1. B-树的定义 当数据量太大时,不可能一次调入内存,要多次访问外存。硬盘的访问速度慢。主要矛盾变为减少访问外存次数。
用二叉树组织文件,若在查找时一次调入一个结点进内存,当文件的记录个数为100000时,要找到给定关键字的记录,平均需访问外存17次(log100000)
95
1. B-树的定义 一棵m阶B-树,它或者为空,或者满足以下性质的m叉树: (1)树中每个结点最多有m个子树;
(2)若根结点不是叶子结点至少有2棵子树。 (3)除根结点以外的所有非终端结点至少有 m/2棵子树。 (4)所有的非终端结点中包含下列信息数据 (n,A0,K1,A1,K2,…,Kn,An) Ki为关键字,且Ki<Ki+1,子树Ai中所有结点的关键字均小于Ki+1,大于Ki;An所指子树中所有结点的关键码均大于Kn, 每棵子树Ai都是m-路B-树,0≦i≦n。
96
1. B-树的定义 设一棵m路B树的高度为h,则各层阶点情况如下; 第一层最多1个; 第二层最多m个; 第三层最多m2个; ………
第h层最多mh-1; 总结点最多为(mh-1)/(m-1) 关键字最多为? mh-1 ***
97
2. B-树的查找 B-树的查找类似二叉排序树的查找,所不同的是B-树每个结点上是多关键码的有序表:
(1)在到达某个结点时,先在(多关键码的)有序表中查找,若找到,则查找成功; (2)否则,到按照对应的指针信息指向的子树中去查找,当到达叶子结点时,则说明树中没有对应的关键码,查找失败。 即在B-树上的查找过程是一个顺指针查找结点和在结点中查找关键码交叉进行的过程。
98
2. B-树的查找 结点结构: #dfine m 3 typedef struct BTNode { int keynum;
struct BTNode *parent; // 指向双亲结点的指针 KeyType key[m+1]; // 关键字(0号单元不用) struct BTNode *ptr[m+1]; // 子树指针向量 Record *recptr[m+1]; // 记录指针向量 } BTNode, *BTree; // B树结点和B树的类型
99
2. B-树的查找 假设返回的是如下所述结构的记录: typedef struct { BTNode *pt; // 指向找到的结点的指针
int i; // 1..m,在结点中的关键字序号 int tag; // 标志查找成功(=1)或失败(=0) } Result; // 在B树的查找结果类型
100
… … 2. B-树的查找 Result SearchBTree(BTree T, KeyType K) {
// 在m 阶的B-树 T 中查找关键字 K, 返回 // 查找结果 (pt, i, tag)。若查找成功,则 // 特征值 tag=1, 指针 pt 所指结点中第 i 个 // 关键字等于 K; 否则特征值 tag=0, 等于 // K 的关键字应插入在指针 pt 所指结点 // 中第 i 个关键字和第 i+1个关键字之间 } // SearchBTree … … 如图
101
2. B-树的查找 p=T; q=NULL; found=FALSE; i=0; while (p && !found) {
n=p->keynum; i=Search(p, K); // 在p->key[1..keynum]中查找 i , // 使得 p->key[i]<=K<p->key[i+1] if (i>0 && p->key[i]==K) found=TRUE; else { q=p; p=p->ptr[i]; } // q 指示 p 的双亲 } if (found) return (p,i,1); // 查找成功 else return (q,i,0); // 查找不成功
102
2. B-树的查找 根结点至少包含两个儿子; 以后各层结点至少有m/2个儿子 第三层:2m/2 第四层:2m/22 ……
第h层:2m/2h-2 查找第h层上的结点需要h次结点访问。
103
2. B-树的查找 假如B-树中的关键字为N个 K1,K2,…,KN 失败结点是N+1,也就是最后一层上的结点个数。
设有效关键字是h层,失败结点都在h+1层; N+1≧2m/2h-1, m/2h-1≦(N+1)/2 两边关于m/2取对数: h≦logm/2{(N+1)/2}+1
104
3. B-树的结点插入过程演示 53 53 75 75 53 139 75 49 53 139 145 例:3阶B-树的建立
关键字:53,75,139,49,145,36 n=1 加入53 n=2 加入75 n=3 加入139 53 75 53 139 n=5 加入49,145 75
105
3. B-树的结点插入过程演示 49 75 36 139 145 例:3阶B-树的建立 关键字:53,75,139,49,145,36
n=6 加入36 36
106
4. B-树的结点插入算法 关键字插入时从最底层开始插入: 若该结点上关键字个数不超过m-1个,则可直接插入到该结点上;
否则:进行结点的“分裂”。 “分裂”的方法:将插入后的结点的关键字分成三部分,前后两部分成为两个结点,中间的一个结点将其插入到父结点中。 若插入父结点而使父结点中关键码个数超过m-1,则父结点继续分裂,有可能直到根结点。
107
5. B-树的结点删除过程演示 在B-树上删除一个关键字时,首先需要找到这个关键字所在的结点,从中删去这个关键码。 删除分两种情况:
①删除最底层结点中关键码 ②删除非底层结点中关键码
108
①删除最底层结点中关键码 50 30 60 70 10 40 55 58 65 75 80 删除55 在最底层结点中删除有3种情况
a) 若结点中关键字个数大于m/2 -1,直接删去。 50 30 10 40 55 58 65 75 80 删除55
109
①删除最底层结点中关键码 50 30 10 40 58 65 75 80
110
①删除最底层结点中关键码 b)被删关键码所在结点删除前关键字个数等于m/2 -1,而与该结点相邻的右兄弟 (或左兄弟) 结点的关键字个数大于 m/2-1。
111
①删除最底层结点中关键码 50 30 10 40 55 58 65 75 80 删除65
112
①删除最底层结点中关键码 50 30 10 40 55 58 70 80 删除70
113
①删除最底层结点中关键码 50 30 10 40 55 60 80
114
②删除非底层结点中关键码 删除50 50 30 10 40 55 58 65 75 80 删除55
115
①删除最底层结点中关键码 55 30 10 40 58 65 75 80
116
二、B+树 1. B+树的定义 2. B+树的查找过程 3. B-树的结点插入过程演示
117
1. B+树的定义 B+树可以看作是B-树的一种变形,在实现文件索引结构方面比B-树使用得普遍。 一棵m阶的B+树和m阶的B-树的差异在于:
(1)所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键码记录的指针; (2)所有的非终端结点可以看成是索引,结点中仅含有其子树根结点中最大(或最小)关键字; (3)通常在B+树上有两个头指针,一个指向根结点,另一个指向关键码最小的叶子结点。
118
2. B+树的查找过程 在B+树上进行随机搜索、插入和删除的过程基本上与B-树类似。
只是在搜索过程中,若非终端结点上的关键码等于给定值,搜索并不停止,而是继续沿右指针向下,一直查到叶结点上的这个关键码。 在B+树,不管查找成功与否,每次查找都是走了一条从根到叶子结点的路径。
119
3. B+树的插入过程 B+树的插入仅在叶结点上进行。每插入一个关键码后都要判断结点中的子树棵数是否超出范围。 包含5个关键字的B+树 加入关键字50,结点分裂 23 50 ***
120
第9章 查 找 预备知识 9.1 静态查找表 9.2 动态查找表 9.3 哈希表
121
9.3 哈希表 9.3.1 什么是哈希表 9.3.2 哈希函数的构造方法 9.3.3 处理冲突的方法 9.3.4 哈希表的查找及其分析
122
9.3.1 什么是哈希表 一、哈希表的引入 二、哈希表的思想 三、哈希表的定义 四、哈希表的有关概念
123
一、哈希表的引入 例1:学生名单的顺序存储与查找方式: Typedef struct list {char num; char name;
char sex; }; list F[n]; 张三 男 李四 男 …………………………… 王五 男 … 张三 男 李四 男 王五 男 … …
124
一、哈希表的引入 num=“030530101” name=“张三” sex=“男” 030530101 张三 男
张三 男 李四 男 …………………………… 王五 男 substr(num,8,2)=“01” H(num)=atoi(substr(num,8,2))-1 F[H(num)].num=num; F[H(num)].name=name; F[H(num)].sex=sex; … 张三 男 李四 男 王五 男 … …
125
二、哈希表的思想 以结点的关键字K为自变量,通过一个确定的函数关系H,计算出对应的函数值H(K),把这个函数值作为结点的存储地址。 存储时:
把关键字为K的数据元素存储在地址H(K)中; 查找时: 给定关键字K,直接到地址H(k)中取数据元素。
126
三、哈希表的定义 用散列法存储的线性表叫散列表(Hash table),又称杂凑表,哈希表,对应的函数称为散列函数、杂凑函数或哈希函数。
127
四、哈希表的有关概念 1、装填因子: 设散列表空间大小为n,填入表中的结点数为m,则称=m/n为散列表的装填因子。
2、散列函数的选取原则: ①运算简单; ②函数值域不能超过表长; ③尽可能使关键字不同时,散列函数值也不同。 3、冲突与同义词 若H(k1)=H(k2),则称为冲突, 发生冲突的两个关键字k1和k2称为同义词。 ***
128
9.3.2 哈希函数的构造方法 1.直接定址法 取关键字或关键字的某个线性函数值为哈希地址。
即:H(key)=key或H(key)=a*key+b 其中a、b为常数。又称H(key)为自身函数。 优点:没有冲突; 缺点:若关键字集合很大,浪费存储空间严重。
129
9.3.2 哈希函数的构造方法 2.质数除余法 如果表长为n,取小于或等于n的最大质数m作模,关键字通过m取模运算,所得值作为散列地址。
130
9.3.2 哈希函数的构造方法 3.平方取中法 在不知道关键字的全部情况时,可通过求关键字的平方值扩大差别,然后取中间几位作为哈希地址:
int Hash(int key) { //假设key是4位整数 key*=key; //求平方 key=key/100; //去掉末尾的两位数 Key=key%1000; return key;} 关键字 (关键字)2 地址 0100 100 0110 121 1010 201 1001 020 0111 123
131
9.3.2 哈希函数的构造方法 4.折叠法 5. 数字分析法 6. 基数转换法 ***
132
处理冲突的方法 一、开放定址法 二、再哈希法 三、链地址法 四、建立一个公共的溢出区
133
一、开放定址法 1、什么是开放定址法? 又叫开放地址,用于顺序存储;
开放地址指地址单元为空,对于数组来说,是指还没存入数据的数组单元,在顺序存储结构中用一定方法进行散列存取的方法称为开放定址法。
134
一、开放定址法 2、开放定址法示例演示 例.设散列表的长度为13,散列函数为: H(K)=K%13,给定的关键字序列为:
19,14,23, 1,68,20,84,27,54,11,10, H(K)=6, 1,10, 1, 3, 7, 6, 1, 2, 11,10, 14 1 68 27 54 19 20 84 23 11 10 (1)线性探测再散列法:若H(key)=d的单元发生冲突,则按下述方法进行探查: hi(k)=(h(k)+i)%n,n是散列表的长度,1≤i≤n-1
135
一、开放定址法 二次聚集:两个本来不发生冲突的非同义词,也就是散列地址不同的结点,发生争夺同一个散列地址的现象称为二次聚集或堆积。
136
一、开放定址法 3、开放定址法查找示例演示 19, 14, 23, 1, 68, 20, 84, 27, 54, 11, 10, H(K)=6, 1,10, 1, 3, 7, 6, 1, 2, 11,10, 14 1 68 27 54 19 20 84 23 11 10 查找分析:查11,查40。 1、利用散列函数计算出关键字为K的数据元素的地址:d=H(K),如果F[d].key==K,查找成功,返回d; 2、如果F[d].key!=K,依次查找F[(d+i)%n].key, 直到找到某个F[(d+j)%n].key==K,返回(d+j)%n, 或者找到一个开放地址为止,此时显示没找到。
137
一、开放定址法 4、开放定址法删除示例演示 19,14,23, 1,68,20,84,27,54,11,10, H(K)=6, 1,10, 1, 3, 7, 6, 1, 2, 11,10, 14 1 68 27 54 19 20 84 23 11 10 删除分析:如果删除68,查找27。 删除结点时不能简单地将被删结点的地址置空,否则将截断它之后填入散列表的同义词的查找路径。
138
一、开放定址法 4、开放定址法处理冲突的方法 (1)线性探测再散列法:若H(key)=d的单元发生冲突,则按下述方法进行探查:
hi(k)=(h(k)+i)%n,n是散列表的长度,1≤i≤n-1 (2)二次探测再散列法:若H(key)=d的单元发生冲突,则按下述方法进行探查: hi(k)=(h(k)+di)%n,n是散列表的长度, di=12,-12,22,-22,…,+k2,-k2,(k≤n/2) (3)di=伪随机序列,称伪随机探测再散列。 ***
139
二、再哈希法(再散列法) 设置RHi() i=1,2,…,k,多个哈希函数,当一个哈希函数产生冲突时,顺序用下一个。 ***
140
三、链地址法 1、链地址法(又称拉链法)的示例演示 19,14,23, 1,68,20,84,27,54,11,10,79,
H(K)=6, 1,10, 1, 3, 7, 6, 1, 2, 11,10, 1, 1 2 3 4 … ^ 14 1 27 79 ^ 54 ^ 68 ^ ^ …… ………
141
三、链地址法 2、内散列表与外散列表平均查找长度的比较 外散列表平均查找长度短。 查79 14 1 68 27 54 19 20 84 79
23 11 10 14 1 27 79 ^
142
三、链地址法 3、拉链法的查找算法 (1) 根据关键字K找到关键字为K的结点所在单链表的首地址;
(2)在所找到的单链表上进行顺序查找,若找到返回地址值,否则返回空值。 1 2 3 4 ^ 14 1 27 79 ^ 54 ^ 68 ^ ^
143
三、链地址法 4、拉链法的插入算法: (1) 根据关键字K找到关键字为K的结点所应该存在的单链表的首地址;
(3)若没找到,利用头插法或尾插法插入单链表中。 ***
144
四、建立一个公共溢出区 建立一个公共溢出区,当冲突产生时,后来冲突记录存入公共溢出区。 ***
145
9.3.4 哈希表的查找和分析 一、哈希表存储结构 二、哈希表的查找算法 三、哈希表的插入算法 四、哈希表的查找算法分析
146
一、哈希表存储结构 int hashsize[] = { 997, ... }; typedef struct {
ElemType *elem; int count; // 当前数据元素个数 int sizeindex; // hashsize[sizeindex]为当前容量 } HashTable; #define SUCCESS 1 #define UNSUCCESS 0 #define DUPLICATE -1
147
二、哈希表的查找算法 Status SearchHash (HashTable H, KeyType K, int &p, int &c)
{ } // SearchHash p = Hash(K); // 求得哈希地址 while ( H.elem[p].key != NULLKEY && !EQ(K, H.elem[p].key)) collision(p, ++c); // 求得下一探查地址 p if (EQ(K, H.elem[p].key)) return SUCCESS; // 查找成功,返回待查数据元素位置 p else return UNSUCCESS; // 查找不成功
148
三、哈希表的插入算法 Status InsertHash (HashTable &H, Elemtype e) { c = 0;
………………………………… } // InsertHash c = 0; if ( HashSearch ( H, e.key, p, c ) == SUCCESS ) return DUPLICATE; // 表中已有与 e 有相同关键字的元素
149
三、哈希表的插入算法 Status InsertHash (HashTable &H, Elemtype e) {………… else
H.elem[p] = e; ++H.count; return OK; // 查找不成功时,返回 p为插入位置 if ( c < hashsize[H.sizeindex]/2 ) { // 冲突次数 c 未达到上限,(阀值 c 可调) } else {RecreateHashTable(H); return UNSUCCESS; } // 重建哈希表
150
四、哈希表的插入算法分析 1、决定哈希表查找的ASL的因素: (1)选用的哈希函数; (2)选用的处理冲突的方法;
(3)哈希表饱和的程度,装载因子 α=n/m 值的大小(n—记录数,m—表的长度)
151
四、哈希表的插入算法分析 一般情况下,可以认为选用的哈希函数是“均匀”的,也就是在讨论ASL时,认为各个位置被存数据的概率是相等的。
152
四、哈希表的插入算法分析 2、查找成功时有下列结果: (1) 线性探测再散列
153
四、哈希表的插入算法分析 (2) 随机探测再散列 (3) 链地址法
154
四、哈希表的插入算法分析 哈希表的平均查找长度是 的函数,而不是 n 的函数。
这说明,用哈希表构造查找表时,可以选择一个适当的装填因子 ,使得平均查找长度限定在某个范围内。
155
四、哈希表的插入算法分析 3 散列法与其它方法的比较 (1) 不同的散列函数构成的散列表平均查找长度是不同的。
(2) 由同一个散列函数、不同的解决冲突方法构造的散列表,其平均查找长度是不相同的。 (3) 散列法与其他查找方法的区别: 除散列法外,其他查找方法有共同特征为:均是建立在比较关键字的基础上。 散列法是根据关键字直接求出地址的查找方法,其查找的期望时间为O(1)。 ***
156
本章小结 1、熟练掌握顺序查找、二分查找、二叉排序树查找以及散列表查找的基本思想和算法实现。
2、熟练掌握顺序查找、二分查找、二叉排序树查找以及散列表查找的应用条件以及算法优劣。 3、掌握二叉排序树的删除算法和平衡二叉树的构造算法的概要。
Similar presentations