平衡二叉树
对《大话数据结构》P313~P340—二叉排序树和平衡二叉树,进行了自己的理解并完善了代码。
一、二叉排序树
对如下二叉排序树进行中序遍历,就可以得到有序的序列{35,37,47,51,58,62,73,88,93,99}。
查找:
插入:
删除:
1、删除的是叶子节点
2、删除的结点仅有左孩子或右孩子
3、删除的结点既有左孩子也有右孩子
代码和解释如下(VS2012测试通过):
1 #include <iostream>
2 using namespace std;
3 #define TRUE 1
4 #define FALSE 0
5 typedef int Status;
6
7 //二叉树的二叉链表结点结构定义
8 typedef struct BiTNode
9 {
10 int data;
11 struct BiTNode *lchild, *rchild;
12 } BiTNode;
13
14 //二叉排序树的查找操作
15 //递归查找二叉排序树中是否存在key
16 //指针f指向T的双亲,初始调用值为NULL
17 //若查找成功,指针P指向该数据元素结点,并返回TRUE
18 //否则指针P指向查找路径上访问的最后一个结点,并返回FALSE,这个P指向的结点对插入很有用
19 Status SearchBST(BiTNode *T,int key,BiTNode *f,BiTNode **p)
20 {
21 if(!T)//查找不成功
22 {
23 *p=f;
24 return FALSE;
25 }
26 else if(key==T->data)//查找成功
27 {
28 *p=T;
29 return TRUE;
30 }
31 else if(key<T->data)
32 return SearchBST(T->lchild,key,T,p);//在左子树继续查找
33 else
34 return SearchBST(T->rchild,key,T,p);//在右子树继续查找
35 }
36
37 //二叉排序树的插入操作
38 //当二叉排序树T中不存在关键字等于key的数据元素时,插入key并返回TRUE,否则返回FALSE
39 Status InsertBST(BiTNode **T,int key)
40 {
41 BiTNode *p,*s;
42 if(!SearchBST(*T,key,NULL,&p))//如果二叉排序树中不存在key
43 {
44 s=new BiTNode;
45 s->data=key;
46 s->lchild=s->rchild=NULL;
47 if(!p)//说明原本是一棵空树
48 *T=s;//插入s为新的根节点
49 else if(key<p->data)
50 p->lchild=s;//左子树的结点值小于根的值
51 else
52 p->rchild=s;//右子树的结点值大于根的值
53 return TRUE;
54 }
55 else
56 return FALSE;//SearchBST返回TRUE,二叉排序树中已存在key,不再插入
57 }
58
59 //从二叉排序树中删除结点p,并重接它的左或右子树
60 Status Delete(BiTNode **p)
61 {
62 BiTNode *q,*s;
63 if((*p)->rchild==NULL)//*p指向的待删除节点的右子树空,则只需将*p指向它的左子树,再将*p指向的结点删除
64 {
65 q=*p; *p=(*p)->lchild; free(q);//先把*p暂存在q,*p指向左子树,再把q释放即可
66 }
67 else if((*p)->lchild==NULL)//*p指向的待删除节点的左子树空
68 {
69 q=*p; *p=(*p)->rchild; free(q);
70 }
71 else //左右子树均不空,通过找*p指向的待删除节点的前驱和后继,用前驱或后继的值替换待删除节点的值,注意不是删除,是替换值
72 {
73 q=*p; s=(*p)->lchild;
74 while(s->rchild) //找左子树的右子树尽头,找待删结点的前驱
75 {
76 q=s;
77 s=s->rchild;
78 }
79 (*p)->data=s->data;//s指向被删结点的直接前驱,用将被删结点的前驱的值取代被删结点的值
80 if(q!=*p)
81 q->rchild=s->lchild;//重接q的右子树
82 else
83 q->lchild=s->lchild;//重接q的左子树
84 free(s);
85 }
86 return TRUE;
87 }
88
89 //若二叉排序树T中存在关键字等于key的数据元素时,则删除该数据元素结点,并返回TRUE,否则返回FALSE
90 Status DeleteBST(BiTNode **T,int key)
91 {
92 if(!*T)//不存在关键字等于key的数据元素
93 return FALSE;
94 else
95 {
96 if (key==(*T)->data)//找到关键字等于key的数据元素,调用delete函数
97 return Delete(T);
98 else if (key<(*T)->data)
99 return DeleteBST(&(*T)->lchild,key);//递归,从左子树找key
100 else
101 return DeleteBST(&(*T)->rchild,key);//递归,从右子树找key
102 }
103 }
104
105 int main()
106 {
107 int i;
108 int a[10]={62,88,58,47,35,73,51,99,37,93};
109 BiTNode *T=NULL;
110 for(i=0;i<10;i++)
111 {
112 InsertBST(&T, a[i]);
113 }
114 DeleteBST(&T,47);
115 }
二叉排序树已链接的方式存储,插入删除的时间性能比较好。只要找到合适的插入和删除位置后,修改链接指针即可。但对于二叉排序树的查找,从根节点到要查找的结点,比较次数等于给定值的结点在二叉排序树的层数,即查找性能取决于二叉排序树的形状。有可能深度与完全二叉树相同,为log2n+1,查找复杂度就是O(logn),也有可能是斜树,查找复杂度为O(n)。所以我们需要要让二叉排序树平衡。
二、平衡二叉树(AVL树)
1、当最小不平衡子树根节点的平衡因子BF大于1时,就右旋。比如插入1。
2、当最小不平衡子树根节点的平衡因子BF小于1时,就左旋。比如插入5,6,7。
3、当最小不平衡子树的BF与它的子树的BF符号相反时,就需要对结点先进行一次旋转以使得符号相同,再反向旋转一次。比如插入9,8。
创建平衡二叉树,一开始看代码有点晕,对于右旋、左旋、双旋,以及左平衡旋转处理,建议画图,就能把代码理通。把代码看通是比较简单的,自己写是有点困难呢。
代码和解释如下(VS2012测试通过):
1 #include <iostream>
2 #define TRUE 1
3 #define FALSE 0
4 #define LH 1//左高
5 #define EH 0 //等高
6 #define RH -1//右高
7 typedef int Status;
8
9 //二叉树的二叉链表结点结构定义
10 typedef struct BiTNode
11 {
12 int data;
13 int bf;//结点的平衡因子
14 struct BiTNode *lchild, *rchild;
15 } BiTNode;
16
17 //对以p为根的二叉排序树作右旋处理,处理之后p指向新的树根结点,即旋转处理之前的左子树的根结点
18 void R_Rotate(BiTNode **P)
19 {
20 BiTNode *L;
21 L=(*P)->lchild;//L指向P的左子树根结点
22 (*P)->lchild=L->rchild;//L的右子树挂接为P的左子树
23 L->rchild=(*P);
24 *P=L;//P指向新的根结点
25 }
26
27 //对以P为根的二叉排序树作左旋处理,处理之后P指向新的树根结点,即旋转处理之前的右子树的根结点
28 void L_Rotate(BiTNode **P)
29 {
30 BiTNode *R;
31 R=(*P)->rchild;//R指向P的右子树根结点
32 (*P)->rchild=R->lchild;//R的左子树挂接为P的右子树
33 R->lchild=(*P);
34 *P=R;//P指向新的根结点
35 }
36
37 //对以指针T所指结点为根的二叉树作左平衡旋转处理,结束时,指针T指向新的根结点
38 //该函数被调用,说明传入的需要调整平衡性的子树T的根节点的BF的值大于1
39 void LeftBalance(BiTNode **T)
40 {
41 BiTNode *L,*Lr;
42 L=(*T)->lchild;//指向T的左子树根结点
43 switch(L->bf)
44 {//检查T的左子树的平衡度,并作相应平衡处理
45 case LH://新结点插入在T的左孩子的左子树上,要作单右旋处理
46 (*T)->bf=L->bf=EH;
47 R_Rotate(T);
48 break;
49 case RH://新结点插入在T的左孩子的右子树上,要作双旋处理
50 Lr=L->rchild;//Lr指向T的左孩子的右子树根
51 switch(Lr->bf)
52 {//修改T及其左孩子的平衡因子
53 case LH: (*T)->bf=RH;
54 L->bf=EH;
55 break;
56 case EH: (*T)->bf=L->bf=EH;
57 break;
58 case RH: (*T)->bf=EH;
59 L->bf=LH;
60 break;
61 }
62 Lr->bf=EH;
63 L_Rotate(&(*T)->lchild);//对T的左子树作左旋平衡处理
64 R_Rotate(T);//对T作右旋平衡处理
65 }
66 }
67
68 //对以指针T所指结点为根的二叉树作右平衡旋转处理,结束时,指针T指向新的根结点
69 //该函数被调用,说明传入的需要调整平衡性的子树T的根节点的BF的值小于-1
70 void RightBalance(BiTNode **T)
71 {
72 BiTNode *R,*Rl;
73 R=(*T)->rchild;//R指向T的右子树根结点
74 switch(R->bf)
75 {//检查T的右子树的平衡度,并作相应平衡处理
76 case RH: //新结点插入在T的右孩子的右子树上,要作单左旋处理
77 (*T)->bf=R->bf=EH;
78 L_Rotate(T);
79 break;
80 case LH: //新结点插入在T的右孩子的左子树上,要作双旋处理
81 Rl=R->lchild;//Rl指向T的右孩子的左子树根
82 switch(Rl->bf)
83 {//修改T及其右孩子的平衡因子
84 case RH: (*T)->bf=LH;
85 R->bf=EH;
86 break;
87 case EH: (*T)->bf=R->bf=EH;
88 break;
89 case LH: (*T)->bf=EH;
90 R->bf=RH;
91 break;
92 }
93 Rl->bf=EH;
94 R_Rotate(&(*T)->rchild);//对T的右子树作右旋平衡处理
95 L_Rotate(T);//对T作左旋平衡处理
96 }
97 }
98
99 //若在平衡的二叉排序树T中不存在和e有相同关键字的结点,则插入一个数据元素为e的新结点,并返回1,否则返回0。
100 //若因插入而使二叉排序树失去平衡,则作平衡旋转处理,布尔变量taller反映T长高与否
101 Status InsertAVL(BiTNode **T,int e,Status *taller)
102 {
103 if(!*T)
104 {//插入新结点,树长高,置taller为TRUE
105 //当前T为空,申请内存增加一个结点
106 *T=new BiTNode;
107 (*T)->data=e; (*T)->lchild=(*T)->rchild=NULL; (*T)->bf=EH;
108 *taller=TRUE;
109 }
110 else
111 {
112 if (e==(*T)->data)
113 {//树中已存在和e有相同关键字的结点则不再插入
114 *taller=FALSE; return FALSE;
115 }
116 if (e<(*T)->data)
117 {//应继续在T的左子树中进行搜索
118 if(!InsertAVL(&(*T)->lchild,e,taller))//递归的结果,找到了值一样的结点,InsertAVL返回false,!就是true,执行return FALSE
119 return FALSE;
120 if(*taller)//遍历到叶子也没有找到值相同的,插入数据,taller置true。运行到这一步,说明左子树长高,值已插入到T的左子树中
121 switch((*T)->bf)//需要检查T的平衡度,这里获得的是插入前的T的bf,如果不平衡了,需要旋转
122 {
123 case LH://原本左子树比右子树高,左子树插入值后就不平衡了,需要作左平衡处理
124 LeftBalance(T); *taller=FALSE; break;
125 case EH://原本左右子树等高,现因左子树增高而使树增高,T的bf变成1
126 (*T)->bf=LH; *taller=TRUE; break;
127 case RH://原本右子树比左子树高,现左右子树等高,T的bf变成0
128 (*T)->bf=EH; *taller=FALSE; break;
129 }
130 }
131 else
132 {//应继续在T的右子树中进行搜索
133 if(!InsertAVL(&(*T)->rchild,e,taller))//递归的结果,找到了值一样的结点,InsertAVL返回false,!就是true,执行return FALSE
134 return FALSE;
135 if(*taller)//遍历到叶子也没有找到值相同的,插入数据,taller置true。运行到这一步,说明右子树长高,值已插入到T的右子树中
136 switch((*T)->bf)//需要检查T的平衡度,这里获得的是插入前的T的bf,如果不平衡了,需要旋转
137 {
138 case LH://原本左子树比右子树高,现左右子树等高,T的bf变成0
139 (*T)->bf=EH; *taller=FALSE; break;
140 case EH://原本左右子树等高,现因右子树增高而使树增高,T的bf变成1
141 (*T)->bf=RH; *taller=TRUE; break;
142 case RH://原本右子树比左子树高,右子树插入值后就不平衡了,需要作右平衡处理
143 RightBalance(T); *taller=FALSE; break;
144 }
145 }
146 }
147 return TRUE;
148 }
149
150 int main(void)
151 {
152 int i;
153 int a[10]={3,2,1,4,5,6,7,10,9,8};
154 BiTNode *T=NULL;
155 Status taller;
156 for(i=0;i<10;i++)//生成二叉排序树,并保证该树是平衡二叉树
157 {
158 InsertAVL(&T,a[i],&taller);
159 }
160 return 0;
161 }
-------------------------------------------------
原创博客 转载请注明出处http://www.cnblogs.com/hslzju
-------------------------------------------------