二叉排序树
二叉排序树
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
I.建树:
初始时树为空。
对于第一个数,直接作为树的根结点。
对于堆中的一个数,起始时设置与之比较的结点为树的根结点,该数与结点的值比较,若该数与结点的值相等,则无操作,结束;若该数比结点的值小,则结点变为结点的左子结点;若该数比结点的值大或等于(其它情况),则结点变为结点的右子结点。直到结点为空,则增加一个结点,为之前结点的左/右子结点,并写入数值。
II.判断是否出现:
对于一个需要判断的数,起始时设置与之比较的结点为树的根结点,该数与结点的值比较,若该数与结点的值相等,则该数在堆中出现,结束比较;若该数比结点的值小,则结点变为结点的左子结点;若该数比结点的值大,则结点变为结点的右子结点。直到结点为空,则该数在堆中没有出现,结束比较。
III.输出:
任意一个结点,左子结点(如果有)的值小于等于该结点的值,右子结点(如果有)的值大于等于该结点的值。
所以采用中序遍历(LDR),对于任意一个结点,输出的先后顺序(不一定相邻)为左子结点,根结点,右子结点。
建树保证满足二叉排序树 “对于树中的任意一个结点,若该结点有左子结点,则左子结点的值小于等于该结点的值,若该结点有右子结点,则右子结点的值大于等于该结点的值” 的性质:
新加入的数满足上述条件,而其它数没有变化,也满足上述条件。所以所有的点满足上述条件。
建树和输出的时间复杂度都大约为树所有结点的高度之和。而当树满足线性结构时(内部结点的出度都为1),可用归纳假设证明树所有结点的高度之和最大。
1.结点为0的树满足当满足线性结构时,树所有结点的高度之和最大;
2.假设一棵结点数目为n的树,当满足线性结构时,树所有结点的高度之和最大。对于一棵结点数目为n的树增加一个结点,增加的结点高度最大为n,当树满足线性结构时成立。所以一棵结点数目为n+1的树,当满足线性结构时,树所有结点的高度之和最大。
所以得证。
建树+输出的最坏时间复杂度:
把数按照从小到大或从大到小的顺序加入树中,每次新的结点是上次结点的左/右子结点。树的深度为树所有结点的高度之和,大约为n*n/2,即建树和输出的时间复杂度都为O(n*n/2),总的时间复杂度为O(n*n)。
建树+输出的平均复杂度:O(nlogn)
判断是否出现平均时间复杂度:O(logn),最坏时间复杂度:O(n)。
Code:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <malloc.h> 4 5 //二叉排序树(判断一个数是否出现在一个序列中) 6 //1.排序:相同的数要加进去 7 //2.判断某些数是否在一个序列中 8 //所以当序列加入二叉排序树中,相同的数不必加入 9 //但是相同的数不必再加入:需要每次都判断两个数是否相同,时间消耗长,反不如让相同的数也加入树中(如果序列中的数基本不相同) 10 11 struct node 12 { 13 long d; 14 struct node *left; 15 struct node *right; 16 }*tree; 17 18 void LDR(struct node *p) 19 { 20 if (p!=NULL) 21 { 22 LDR(p->left); 23 printf("%ld ",p->d); 24 LDR(p->right); 25 } 26 } 27 28 int main() 29 { 30 struct node *p,*q; 31 long n,m,s,i; 32 scanf("%ld",&n); 33 if (n>=1) 34 { 35 scanf("%ld",&s); 36 tree=(struct node *) malloc (sizeof(struct node)); 37 tree->d=s; 38 tree->left=NULL; 39 tree->right=NULL; 40 } 41 for (i=2;i<=n;i++) 42 { 43 scanf("%ld",&s); 44 p=(struct node *) malloc (sizeof(struct node)); 45 p=tree; 46 while (p) 47 { 48 if (s<p->d) 49 { 50 if (p->left!=NULL) 51 p=p->left; 52 else 53 { 54 q=(struct node *) malloc (sizeof(struct node)); 55 q->d=s; 56 q->left=NULL; 57 q->right=NULL; 58 p->left=q; 59 break; 60 } 61 } 62 else 63 { 64 if (p->right!=NULL) 65 p=p->right; 66 else 67 { 68 q=(struct node *) malloc (sizeof(struct node)); 69 q->d=s; 70 q->left=NULL; 71 q->right=NULL; 72 p->right=q; 73 break; 74 } 75 } 76 } 77 } 78 scanf("%ld",&m); 79 for (i=1;i<=m;i++) 80 { 81 scanf("%ld",&s); 82 if (n==0) 83 { 84 printf("%ld do not exist\n",s); 85 continue; 86 } 87 p=(struct node *) malloc (sizeof(struct node)); 88 p=tree; 89 while (p) 90 { 91 if (p->d==s) 92 { 93 printf("%ld exist\n",s); 94 break; 95 } 96 else if (s<p->d) 97 { 98 if (p->left!=NULL) 99 p=p->left; 100 else 101 { 102 printf("%ld do not exist\n",s); 103 break; 104 } 105 } 106 else 107 { 108 if (p->right!=NULL) 109 p=p->right; 110 else 111 { 112 printf("%ld do not exist\n",s); 113 break; 114 } 115 } 116 } 117 } 118 //print tree 119 //任意一个结点,左子结点(如果有)的值小于该结点的值,右子结点(如果有)的值大于该结点的值 120 //所以采用中序遍历(LDR),对于任意一个结点,输出的先后顺序(不一定相邻)为左子结点,根结点,右子结点 121 printf("Ascending Order: "); 122 LDR(tree); 123 return 0; 124 } 125 /* 126 6 127 2 6 1 3 3 4 128 3 129 1 4 7 130 */
优化:
Size Balanced Tree(SBT)
AVL树
红黑树
Treap(Tree+Heap)
这些均可以使查找树的高度为O(log(n))。
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
二叉排序树
——二叉排序树的值都不相同
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
二叉排序树的修改版(这篇文章中简称为二叉排序树):
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树的修改版;
I.建树:
初始时树为空。
对于第一个数,直接作为树的根结点。
对于堆中的一个数,起始时设置与之比较的结点为树的根结点,该数与结点的值比较,若该数与结点的值相等,则无操作,结束;若该数比结点的值小,则结点变为结点的左子结点;若该数比结点的值大,则结点变为结点的右子结点。直到结点为空,则增加一个结点,为之前结点的左/右子结点,并写入数值。
II.判断是否出现:
对于一个需要判断的数,起始时设置与之比较的结点为树的根结点,该数与结点的值比较,若该数与结点的值相等,则该数在堆中出现,结束比较;若该数比结点的值小,则结点变为结点的左子结点;若该数比结点的值大,则结点变为结点的右子结点。直到结点为空,则该数在堆中没有出现,结束比较。
建树保证二叉排序树的值都不相同:
1.不加任何数时,二叉排序树的值都不相同。
2.假设某个数未加之前二叉排序树的值都不相同,加入数s后,
对于某个数s在建树中经过的所有结点,都有:
I.若从结点(值为t)到达结点的左子结点,则s<t<该结点的右子树的所有结点的数值,即与该结点的右子树的所有结点的数值都不相同。
II.若从结点(值为t)到达结点的右子结点,则s>t>该结点的左子树的所有结点的数值,即与该结点的左子树的所有结点的数值都不相同。
III.若该结点的数值(值为t)等于数s,则s=t>该结点的左子树的所有结点的数值,s=t<该结点的右子树的所有结点的数值。
所以加入该数后,二叉排序树的值仍然都不相同。
所以建树保证二叉排序树的值都不相同。
例子:
建树保证满足二叉排序树 “对于树中的任意一个结点,若该结点有左子结点,则左子结点的值小于等于该结点的值,若该结点有右子结点,则右子结点的值大于等于该结点的值” 的性质:
新加入的数满足上述条件,而其它数没有变化,也满足上述条件。所以所有的点满足上述条件。
建树和输出的时间复杂度都大约为树所有结点的高度之和。而当树满足线性结构时(内部结点的出度都为1),可用归纳假设证明树所有结点的高度之和最大。
1.结点为0的树满足当满足线性结构时,树所有结点的高度之和最大;
2.假设一棵结点数目为n的树,当满足线性结构时,树所有结点的高度之和最大。对于一棵结点数目为n的树增加一个结点,增加的结点高度最大为n,当树满足线性结构时成立。所以一棵结点数目为n+1的树,当满足线性结构时,树所有结点的高度之和最大。
所以得证。
建树+输出的最坏时间复杂度:
把数按照从小到大或从大到小的顺序加入树中,每次新的结点是上次结点的左/右子结点。树的深度为树所有结点的高度之和,大约为n*n/2,即建树和输出的时间复杂度都为O(n*n/2),总的时间复杂度为O(n*n)。
建树+输出的平均复杂度:O(nlogn)
判断是否出现平均时间复杂度:O(logn),最坏时间复杂度:O(n)。
Code:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <malloc.h> 4 5 //二叉排序树(判断一个数是否出现在一个序列中) 6 //1.排序:相同的数要加进去 7 //2.判断某些数是否在一个序列中 8 //所以当序列加入二叉排序树中,相同的数不必加入 9 //但是相同的数不必再加入:需要每次都判断两个数是否相同,时间消耗长,反不如让相同的数也加入树中(如果序列中的数基本不相同) 10 11 struct node 12 { 13 long d; 14 struct node *left; 15 struct node *right; 16 }*tree; 17 18 void LDR(struct node *p) 19 { 20 if (p!=NULL) 21 { 22 LDR(p->left); 23 printf("%ld ",p->d); 24 LDR(p->right); 25 } 26 } 27 28 int main() 29 { 30 struct node *p,*q; 31 long n,m,s,i; 32 scanf("%ld",&n); 33 if (n>=1) 34 { 35 scanf("%ld",&s); 36 tree=(struct node *) malloc (sizeof(struct node)); 37 tree->d=s; 38 tree->left=NULL; 39 tree->right=NULL; 40 } 41 for (i=2;i<=n;i++) 42 { 43 scanf("%ld",&s); 44 p=(struct node *) malloc (sizeof(struct node)); 45 p=tree; 46 while (p) 47 { 48 if (s<p->d) 49 { 50 if (p->left!=NULL) 51 p=p->left; 52 else 53 { 54 q=(struct node *) malloc (sizeof(struct node)); 55 q->d=s; 56 q->left=NULL; 57 q->right=NULL; 58 p->left=q; 59 break; 60 } 61 } 62 else 63 { 64 if (p->right!=NULL) 65 p=p->right; 66 else 67 { 68 q=(struct node *) malloc (sizeof(struct node)); 69 q->d=s; 70 q->left=NULL; 71 q->right=NULL; 72 p->right=q; 73 break; 74 } 75 } 76 } 77 } 78 scanf("%ld",&m); 79 for (i=1;i<=m;i++) 80 { 81 scanf("%ld",&s); 82 if (n==0) 83 { 84 printf("%ld do not exist\n",s); 85 continue; 86 } 87 p=(struct node *) malloc (sizeof(struct node)); 88 p=tree; 89 while (p) 90 { 91 if (p->d==s) 92 { 93 printf("%ld exist\n",s); 94 break; 95 } 96 else if (s<p->d) 97 { 98 if (p->left!=NULL) 99 p=p->left; 100 else 101 { 102 printf("%ld do not exist\n",s); 103 break; 104 } 105 } 106 else 107 { 108 if (p->right!=NULL) 109 p=p->right; 110 else 111 { 112 printf("%ld do not exist\n",s); 113 break; 114 } 115 } 116 } 117 } 118 //print tree 119 //任意一个结点,左子结点(如果有)的值小于该结点的值,右子结点(如果有)的值大于该结点的值 120 //所以采用中序遍历(LDR),对于任意一个结点,输出的先后顺序(不一定相邻)为左子结点,根结点,右子结点 121 printf("Ascending Order: "); 122 LDR(tree); 123 return 0; 124 } 125 /* 126 6 127 2 6 1 3 3 4 128 3 129 1 4 7 130 */
优化:
Size Balanced Tree(SBT)
AVL树
红黑树
Treap(Tree+Heap)
这些均可以使查找树的高度为O(log(n))。
/////////////////////////////////////////////////////////////////////////////////
某种求法的错误性:
当t->lson/rson为空时,call InsertNode()。然后t=nil,对t赋值为s,但之前的t->lson/rson仍为空。所以不能这样做,必须在t->lson/rson为空时,修改t->lson/rson的值。