平衡二叉树

对《大话数据结构》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 }


posted @ 2016-04-27 16:31  Pearl_zju  阅读(176)  评论(0编辑  收藏  举报