数据结构与算法分析(12)特殊二叉树的应用(一)
本节继续介绍二叉树的相关内容,包括二叉查找树和AVL树。
(1)二叉查找树:
定义:
使二叉树成为二叉查找树的性质是,对于树中的每个结点X,它的左子树中所有的关键字值小于X的关键字的值,而它的右子树中所有关键字的值大于X的关键字值。 二叉查找树的平均深度是O(log N)。
1 /**********二叉查找树的标准例程**********/ 2 3 #ifndef _Tree_H 4 typedef int ElementType; 5 struct TreeNode; 6 typedef struct TreeNode *Position; 7 typedef struct TreeNode *SearchTree; 8 9 SearchTree MakeEmpty(SearchTree T); 10 Position Find(ElementType X,SearchTree T); 11 Position FindMin(SearchTree T); 12 Position FindMax(SearchTree T); 13 SearchTree Insert(ElementType X,SearchTree T); 14 SearchTree Delete(ElementType X,SearchTree T); 15 ElementType Retrieve(Position P); 16 #endif /* _Tree_H */ 17 18 struct TreeNode{ 19 ElementType Element; 20 SearchTree Left; 21 SearchTree Right; 22 }; 23 24 /* MakeEmpty 这个操作主要用于初始化 */ 25 SearchTree MakeEmpty(SearchTree T){ 26 if(T!=NULL){ 27 MakeEmpty(T->Left); 28 MakeEmpty(T->Right); 29 free(T); 30 } 31 return NULL; 32 }//遵循树的递归定义 33 34 /* Find 这个操作需要返回树T中具有关键字X的节点的指针,如果这样的节点不存在则返回NULL */ 35 Position Find(ElementType X,SearchTree T){ 36 //注意测试的顺序,首先是需要对空树进行测试,否则就可能在指针上兜圈子 37 if(T==NULL) 38 return NULL; 39 //其余的测试应该使得最不可能的情况安排在最后进行。 40 if(X<T->Element) 41 return Find(X,T->Left); 42 else if(X>T->Element) 43 return Find(X,T->Right); 44 else 45 return T; 46 }//这里的递归调用事实都是尾递归并且可以用一次赋值和一个goto语句很容易代替 47 48 /*FindMin FindMax 这些例程分别返回树中的最小元和最大元的位置*/ 49 Position FindMin(SearchTree T){ 50 if(T==NULL){ 51 return NULL; 52 }else if(T->Left==NULL){ 53 return T; 54 }else{ 55 return FindMin(T->Left); 56 } 57 } 58 Position FindMax(SearchTree T){ 59 if(T!=NULL){ 60 while(T->Right!=NULL){ 61 T=T->Right 62 } 63 return T; 64 } 65 } 66 67 typedef FatalError printf; 68 SearchTree Insert(ElementType X,SearchTree T){ 69 if(T==NULL){ 70 T=malloc(sizeof(struct TreeNode)); 71 if(T==NULL){ 72 FatalError("Out of Space"); 73 }else{ 74 T->Element=X; 75 T->Left=T->Right=NULL; 76 } 77 }else if(X<T->Element){ 78 T->Left=Insert(X,T->Left); 79 }else if(X>T->Element){ 80 T->Right=Insert(X,T->Right); 81 } 82 //Else X is in the tree alreay;we'll do nothing 83 return T; 84 } 85 /* 86 重复元的插入可以通过在结点记录中保留一个附加域以指示发生的频率来处理。这使整 87 个的树增加了某些附加空间,但是却比将重复信息放到树中要好(它将使树的深度变得很 88 大)。当然,如果关键字只是一个更大数据结构的一部分。这种方法行不通,此时我们 89 可以将具有相同关键字的所有结构保留在一个辅助数据结构中,如表或另一颗查找树。 90 */ 91 92 /* Delete 删除操作通常需要考虑许多可能的情况 */ 93 SearchTree Delete(ElementType X,SearchTree T){ 94 Position TmpCell; 95 if(T==NULL){ 96 printf("元素没有找到!\n"); 97 }else if(X<T->Element){ 98 T->Left=Delete(T->Left); 99 }else if(X>T->Element){ 100 T->Right=Delete(T->Right); 101 }else if(T->Left&&T->Right){//两个儿子 102 TmpCell=FindMin(T->Right); 103 T->Element=TmpCell->Element; 104 T->Right=Delete(T->Element,T->Right); 105 }else{ //一个或者没有儿子 106 TmpCell=T; 107 if(T->Left=NULL){ 108 T=T->Right; 109 }else if(T->Right==NULL){ 110 T=T->Left; 111 } 112 free(TmpCell); 113 } 114 }
对于二叉查找树的删除例程,上述删除操作的效率并不高,因为它沿树进行两趟搜索以查找和删除右子树中最小的结点。如果删除的次数不多通常使用的策略是惰性删除:当一个元素要被删除时,它留在树中,而是做了一个被删除的记号。这种做法特别是在有重复关键字时很流行,因为此时记录出现频率数的域可以减一。
(2)二叉查找树的使用范例:
1)构造简单的二叉查找树:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<time.h> 4 typedef struct TreeNode *SearchTree; 5 typedef struct TreeNode *Position; 6 7 struct TreeNode{ 8 int Element; 9 int flag; 10 SearchTree Left; 11 SearchTree Right; 12 }; 13 14 SearchTree Insert(int x,SearchTree T){ 15 if(T==NULL){ 16 //创建一个新的结点 17 T=malloc(sizeof(struct TreeNode)); 18 T->Element=x; 19 T->flag=1; 20 T->Left=T->Right=NULL; 21 }else if(x<T->Element){ 22 T->Left=Insert(x,T->Left); 23 }else if(x>T->Element){ 24 T->Right=Insert(x,T->Right); 25 }else { 26 T->flag++; 27 } 28 return T; 29 } 30 Position FindMax(SearchTree T){ 31 if(T==NULL){ 32 return NULL; 33 }else if(T->Right==NULL){ 34 return T; 35 }else{ 36 return FindMax(T->Right); 37 } 38 } 39 Position FindMin(SearchTree T){ 40 if(T==NULL){ 41 return NULL; 42 }else if(T->Left==NULL){ 43 return T; 44 }else{ 45 return FindMin(T->Left); 46 } 47 } 48 //对二叉树进行遍历并输出 49 void TraverseAndPrint(SearchTree T){ 50 int i=0; 51 if(T!=NULL){ 52 TraverseAndPrint(T->Left); 53 for(i=1;i<=T->flag;i++){ 54 printf("%2d ",T->Element); 55 } 56 TraverseAndPrint(T->Right); 57 } 58 } 59 60 int main(){ 61 SearchTree testTree=NULL; 62 printf("下面构造一个有20个结点的二叉树并输出最大最小的结点:\n"); 63 int i,x; 64 srand(time(NULL)); 65 for(i=0;i<20;i++){ 66 x=rand()%89+10; 67 printf("x[%2d]=%-5d",i+1,x); 68 if((i+1)%5==0){ 69 printf("\n"); 70 } 71 testTree=Insert(x,testTree); 72 } 73 printf("The max of the tree=%d,the min of the tree=%d\n" 74 ,FindMax(testTree)==NULL?0:FindMax(testTree)->Element 75 ,FindMin(testTree)==NULL?0:FindMin(testTree)->Element); 76 printf("按顺序输出此树中保存的结点元素:\n"); 77 TraverseAndPrint(testTree);printf("\n"); 78 }
2)二叉查找树的删除实例(允许有多个重复结点)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <time.h> 4 5 //系统也有TreeNode。 6 7 struct TreeNode{ 8 int Element; 9 int flag; 10 struct TreeNode * Left; 11 struct TreeNode * Right; 12 }; 13 14 typedef struct TreeNode *SearchTree; 15 typedef struct TreeNode *Position; 16 17 SearchTree Insert(int x,SearchTree T){ 18 if(T==NULL){ 19 //创建一个新的结点 20 T=malloc(sizeof(struct TreeNode)); 21 T->Element=x; 22 T->flag=1; 23 T->Left=T->Right=NULL; 24 }else if(x<T->Element){ 25 T->Left=Insert(x,T->Left); 26 }else if(x>T->Element){ 27 T->Right=Insert(x,T->Right); 28 }else { 29 T->flag++; 30 } 31 return T; 32 } 33 Position FindMax(SearchTree T){ 34 if(T==NULL){ 35 return NULL; 36 }else if(T->Right==NULL){ 37 return T; 38 }else{ 39 return FindMax(T->Right); 40 } 41 } 42 Position FindMin(SearchTree T){ 43 if(T==NULL){ 44 return NULL; 45 }else if(T->Left==NULL){ 46 return T; 47 }else{ 48 return FindMin(T->Left); 49 } 50 } 51 Position Find(int x,SearchTree T){ 52 if(T==NULL){ 53 return NULL; 54 }else if(x<T->Element) 55 return Find(x,T->Left); 56 else if(x>T->Element) 57 return Find(x,T->Right); 58 else 59 return T; 60 } 61 //对二叉树进行遍历并输出 62 void TraverseAndPrint(SearchTree T){ 63 int i=0; 64 if(T!=NULL){ 65 TraverseAndPrint(T->Left); 66 for(i=1;i<=T->flag;i++){ 67 printf("%2d ",T->Element); 68 } 69 TraverseAndPrint(T->Right); 70 } 71 } 72 SearchTree Delete(int x,SearchTree T){ 73 Position TmpCell; 74 if(T==NULL){ 75 printf("错误\n"); 76 }else{ 77 if(x<T->Element){ 78 T->Left=Delete(x,T->Left); 79 }else if(x>T->Element){ 80 T->Right=Delete(x,T->Right); 81 }else{ 82 if(T->flag>1){ 83 T->flag--; 84 return T; 85 } 86 if(T->Left&&T->Right){ 87 TmpCell=FindMin(T->Right); 88 T->Element=TmpCell->Element; 89 T->Right=Delete(T->Element,T->Right); 90 }else{ 91 TmpCell=T; 92 if(T->Left==NULL) 93 T=T->Right; 94 else if(T->Right==NULL) 95 T=T->Left; 96 free(TmpCell); 97 } 98 } 99 } 100 return T; 101 } 102 int main(){ 103 SearchTree testTree=NULL; 104 printf("下面构造一个有20个结点的二叉树并输出最大最小的结点:\n"); 105 int i,x; 106 srand(time(NULL)); 107 for(i=0;i<20;i++){ 108 x=rand()%89+10; 109 printf("x[%2d]=%-5d",i+1,x); 110 if((i+1)%5==0){ 111 printf("\n"); 112 } 113 testTree=Insert(x,testTree); 114 } 115 printf("The max of the tree=%d,the min of the tree=%d\n" 116 ,FindMax(testTree)==NULL?0:FindMax(testTree)->Element 117 ,FindMin(testTree)==NULL?0:FindMin(testTree)->Element); 118 printf("按顺序输出此树中保存的结点元素:\n"); 119 TraverseAndPrint(testTree); 120 printf("请输入你想要删除的结点:\n"); 121 scanf("%d",&x); 122 getchar(); 123 if(Find(x,testTree)!=NULL){ 124 printf("在此树中有此结点,按y键删除此节点\n"); 125 } 126 if(getchar()=='y'){ 127 testTree=Delete(x,testTree); 128 } 129 TraverseAndPrint(testTree); 130 }
3)二叉查找树结点的高度:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<time.h> 4 typedef struct TreeNode *SearchTree; 5 typedef struct TreeNode *Position; 6 7 struct TreeNode{ 8 int Element; 9 int flag; 10 SearchTree Left; 11 SearchTree Right; 12 }; 13 14 SearchTree Insert(int x,SearchTree T){ 15 if(T==NULL){ 16 //创建一个新的结点 17 T=malloc(sizeof(struct TreeNode)); 18 T->Element=x; 19 T->flag=1; 20 T->Left=T->Right=NULL; 21 }else if(x<T->Element){ 22 T->Left=Insert(x,T->Left); 23 }else if(x>T->Element){ 24 T->Right=Insert(x,T->Right); 25 }else { 26 T->flag++; 27 } 28 return T; 29 } 30 Position FindMax(SearchTree T){ 31 if(T==NULL){ 32 return NULL; 33 }else if(T->Right==NULL){ 34 return T; 35 }else{ 36 return FindMax(T->Right); 37 } 38 } 39 Position FindMin(SearchTree T){ 40 if(T==NULL){ 41 return NULL; 42 }else if(T->Left==NULL){ 43 return T; 44 }else{ 45 return FindMin(T->Left); 46 } 47 } 48 //对二叉树进行遍历并输出 49 void TraverseAndPrint(SearchTree T){ 50 int i=0; 51 if(T!=NULL){ 52 TraverseAndPrint(T->Left); 53 for(i=1;i<=T->flag;i++){ 54 printf("%6d",T->Element); 55 } 56 TraverseAndPrint(T->Right); 57 } 58 } 59 60 int max(int a,int b){ 61 return a>=b?a:b; 62 } 63 //求二叉树单个结点的高度 64 int height(SearchTree T){ 65 if(T==NULL){ 66 return -1; 67 }else if(T->Left==NULL&&T->Right==NULL){ 68 return 0; 69 } 70 else{ 71 return (max(height(T->Right),height(T->Left))+1); 72 } 73 } 74 int calAver(SearchTree T){ 75 if(T==NULL){ 76 return 0; 77 }else if((T->Left==NULL)&&(T->Right==NULL)){ 78 return 0; 79 }else{ 80 return height(T)+calAver(T->Left)+calAver(T->Right); 81 } 82 } 83 int main(){ 84 SearchTree testTree=NULL; 85 printf("下面构造一个有500个结点的二叉树并输出最大最小的结点:\n"); 86 int i,x; 87 srand(time(NULL)); 88 for(i=0;i<500;i++){ 89 x=rand()%20000+10; 90 //printf("x[%3d]=%6d",i+1,x); 91 if((i+1)%10==0){ 92 //printf("\n"); 93 } 94 testTree=Insert(x,testTree); 95 } 96 printf("The max of the tree=%d,the min of the tree=%d\n" 97 ,FindMax(testTree)==NULL?0:FindMax(testTree)->Element 98 ,FindMin(testTree)==NULL?0:FindMin(testTree)->Element); 99 printf("按顺序输出此树中保存的结点元素:\n"); 100 TraverseAndPrint(testTree); 101 int h=height(testTree); 102 printf("这棵500个结点的二叉查找树的高度是:%d\n",h); 103 int depth=calAver(testTree); 104 printf("这棵500个结点的二叉查找树的高度和是:%d\n",depth); 105 return 0; 106 }