平衡二叉树这里停留了很久,费了很大功夫才弄懂。做个笔记比较好。
参考以下两位大佬的博客写的:
1: http://www.cnblogs.com/fornever/archive/2011/11/15/2249492.html
2: http://blog.csdn.net/followmyinclinations/article/details/50426413
新节点插入就以下4种情况:重点!!!
1左左:即是插入的新节点在T的左孩子的左子树上,这种情况直接单纯的以T为根节点进行右旋
2左右:即是插入的新节点在T的左孩子的右子树上,这种情况先以T的左孩子进行左旋,再以T为根节点进行右旋
3右左:即是插入的新节点在T的右孩子的左子树上,先以T的右孩子为根进行右旋,再以T为根节点进行左旋
4右右:即是插入的新节点在T的右孩子的右子树上,这种情况直接单纯的以T为根节点进行左旋
这次没有将将解释放在代码中,我直接花了很多图:
每个小函数对照图来理解:
1 L_Rotate
2 R_Rotate
3 LeftBalance
/*#include<stdio.h>
#include<string>
#include<iostream>
#include<cmath>
#include<algorithm>
#define OK 1
#define ERROR 0
using namespace std;
typedef struct TreeNode
{
int data;
struct TreeNode *lchild,*rchild;
} Binode,*Bitree;
int SearchBST(Bitree T,Bitree F,int key,Bitree *p)//二叉排序树的查找
{
if(!T)
{
*p=F;
//开始觉得这句话没用,实际上是为二叉排序树的插入作铺垫,查找不成功时,p指向它的双亲节点
return ERROR;
}
else if(T->data==key)
{
*p=T;//如果找到,就将找到的这个点地址用二级指针p保存起来
return OK;
}
else if(T->data<key)//如果查找值大于“此时”的“根节点”,就在其右子树之中查询
{
return SearchBST(T->rchild,T,key,p);
}
else return SearchBST(T->lchild,T,key,p);
}
int InsertBST(Bitree *T,int key)//e二叉排序树的插入(就是建立二叉排序树)
{
Bitree p,s;
if(!SearchBST(*T,NULL,key,&p))//在这,p保存的值为当前插入值key该插入地方的双亲结点的地址,即p所指向的结点是key值得双亲结点
{
s=new TreeNode();//开辟新空间,就是申请新结点
s->data=key;//为新结点赋值
s->lchild=s->rchild=NULL;//新结点的左右子树为空(因为新结点暂时为叶子结点)
if(!p)*T=s;//如果这棵树本身就为空
else if(key>p->data)
{
p->rchild=s;
}
else p->lchild=s;
return OK;
}
else return ERROR;
}
int Delete(Bitree *p)//二叉排序树删除具体某个点(和下一个函数连着看,这个是下一个DeleteBST函数的一部分)
{
//注意p为二级指针
Bitree q,s;//q,s为一级指针
if((*p)->rchild==NULL)
{
//如果删除点的右子树为空,那么它就只有左子树
q=*p;//p是二级指针,那么*p是一级指针,这个一级指针指向要删除的点
*p=(*p)->lchild;//如果删除点只有左子树,那么就直接将该节点的地址换成作孩子的地址
//这里有点难理解,慢慢理解.p本身是个二级指针。那么它就可以指向一个一级指针(即是它可以保存一个一级指针,你可以把它想成一个容器),
//然后你将(*p)->lchild这个一级指针放了进去,达到覆盖的效果,也就是删除了这个结点。这么说吧现在执行完这一句后,*p->datade的值是她左儿子的值
//而不是它本来的值
free(q);
}
else if((*p)->lchild==NULL)
{
q=*p;
*p=(*p)->rchild;
free(q);
}
else
{
q=*p;
s=(*p)->lchild;
while(s->rchild)
{
q=s;
s=s->rchild;
}
(*p)->data=s->data;
if(q==*p)
{
(*p)->lchild=s->lchild;
//q->lchild=s->lchild;
}
else q->rchild=s->lchild;
free(s);
}
}
int DeleteBST(Bitree *T,int key)//二叉排序树删除
{
Bitree p;
if(!*T)return ERROR;
else
{
if(key==(*T)->data)return Delete(T);
else if(key<(*T)->data)return DeleteBST(&(*T)->lchild,key);
else return DeleteBST(&(*T)->rchild,key);
}
}
void inordertraversal(Bitree T)//中序遍历,可以用来检查插入,删除是否有错
{
if(T==NULL)return ;
inordertraversal(T->lchild);
printf("%d ",T->data);
inordertraversal(T->rchild);
}
int main()
{
int i,j,k;
int n;
int a[100];
int query;
while(scanf("%d",&n)!=EOF)
{
for(i=0; i<n; i++)
scanf("%d",&a[i]);
Bitree T=NULL,p,s;
for(i=0; i<n; i++)
{
InsertBST(&T,a[i]);
}
puts("");
inordertraversal(T);
puts("");
/* while(scanf("%d",&query)!=EOF&&query!=0)
{
printf("%d\n",SearchBST(T,NULL,query,&p));
}*/
// while(scanf("%d",&query)!=EOF&&query!=0)
// {
// DeleteBST(&T,query);
// inordertraversal(T);
// puts("");
// }
//
// }
//
// return 0;
//}
/*
16
62 58 88 47 73 99 35 51 93 29 37 49 56 36 48 50
*/
#include<stdio.h>
#include<string.h>
#include<string>
#include<cmath>
#include<algorithm>
#include<iostream>
#define LH 1
#define EH 0
#define RH -1
#define OK 1
#define ERROR 0
#define FALSE 0
#define TRUE 1
using namespace std;
typedef struct BiNode
{
int data;
int bf;
struct BiNode *lchild,*rchild;
} BiNode,*BiTree;
void R_Rotate(BiTree *p)//右旋
{
BiTree L;
L=(*p)->lchild;
(*p)->lchild=L->rchild;
L->rchild=(*p);
(*p)=L;
}
void L_Rotate(BiTree *p)//左旋
{
BiTree R;
R=(*p)->rchild;
(*p)->rchild=R->lchild;
R->lchild=(*p);
(*p)=R;
}
void LeftBalance(BiTree *T)//左子树调整
{
BiTree L,Lr;
L=(*T)->lchild;
switch(L->bf)
{
case LH://左左情况,即是新结点插在T的左孩子的左子树上(左右两边都可以)
(*T)->bf=L->bf=EH;
R_Rotate(T);
break;
case RH:
//左右情况
Lr=L->rchild; switch(Lr->bf) { case LH://新结点是 T的左孩子的右子树 的左孩子 (*T)->bf=RH; L->bf=EH; break; case RH://新结点是 T的左孩子的右子树 的右孩子
L->bf=LH; (*T)->bf=EH; break; case EH://新结点是T的左孩子的右孩子 L->bf=(*T)->bf=EH; break; } Lr->bf=EH; L_Rotate(&(*T)->lchild); R_Rotate(T); break; }}void RightBalance(BiTree *T)//右子树调整{ BiTree R,RL; R=(*T)->rchild; switch(R->bf) { case RH:
//新结点插在T的右孩子的右子树上(左右两边都可以)
(*T)->bf=R->bf=EH; L_Rotate(T); break; case LH: RL=R->lchild; switch(RL->bf) { case LH://新结点是T的右孩子的左子树的左孩子 (*T)->bf=EH; R->bf=RH; break; case RH://新结点是T的右孩子的左子树的右孩子 R->bf=EH; (*T)->bf=LH; break; case EH://新结点是T的右孩子的左孩子 R->bf=(*T)->bf=EH; break; } RL->bf=EH; R_Rotate(&(*T)->rchild);//双旋开始 L_Rotate(T); }}int InsertAVL(BiTree *T,int e,int *taller){ if(!(*T))//如果是空的,就马上插入 { *T=new BiNode(); (*T)->data=e; (*T)->lchild=(*T)->rchild=NULL; (*T)->bf=EH; *taller=TRUE; } else//不是空的看其左右孩子是否可以插入或者插入其左右子树 { if(e==(*T)->data)//如果刚好根节点不为空,且根结点的值就等于插入值 { *taller=FALSE;//树没变高 return FALSE;//插入失败 } if(e<(*T)->data) { if(!InsertAVL(&(*T)->lchild,e,taller))return FALSE; if(taller)//如果插入成功 { switch((*T)->bf) { case LH://这个LH是T原本的bf值(以下的EH,Rh均是),这个要注意!!!!
//原本是LH,现在又在左子树上插入,那么T左子树就就不平衡了,需要马上调整
LeftBalance(T); *taller=FALSE;//调整后树就平衡了,不会长高 break; case EH: (*T)->bf=LH;//原本左右子树一样高,现在左子树添加了一个新结点,故T的左子树高些 *taller=TRUE;//左子树高了 break; case RH://原本右子树高些,现在左子树上插入一个新结点,那么左右子树等高 (*T)->bf=EH; *taller=FALSE; break; } }//如果插到右子树,左右子树是对称的,跟左子树很像,这里就不解释了 } else { if(!InsertAVL(&(*T)->rchild,e,taller))return FALSE; 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; } } } }}void inordertraversal(BiTree T)//中序遍历,可以用来检查插入是否有错{ if(T==NULL)return ; inordertraversal(T->lchild); printf("%d ",T->data); inordertraversal(T->rchild);}int main(){ int i; int a[10]= {3,2,1,4,5,6,7,10,9,8}; BiTree T=NULL; int taller; int tmp,tmp2;// for(i=0; i<10; i++)// {// InsertAVL(&T,a[i],&taller);// } while(scanf("%d",&tmp)!=EOF) { for(i=0;i<tmp;i++){ scanf("%d",&tmp2); InsertAVL(&T,tmp2,&taller); } inordertraversal(T);//中序遍历 puts(""); } return 0;}/*103 2 1 4 5 6 7 10 9 8*/
左左:
即在x的左孩子a的左孩子c上插入一个结点y(该结点也可以是c,如图①),即y可以是c,也可以是c的左孩子(如图②),也可以是c的右孩子(不在画出)
图①就不用说了,结点x和结点a变换,则树平衡了;那么图②就是树中的一般情况了a结点有右孩子d,那要进行x和a变换,那么a的右孩子放哪啊?
很简单,如图放在x的左孩子上;分析:x>d,d>a,所以d可作为x的左孩子,且可作为a的右孩子中的孩子。下边这样的类似情况不再一一分析,自己分析分析~
实现:找到根结点x,与它的左孩子a进行交换即可使二叉树树再次平衡;
右右:
即在x的右孩子a的右孩子c上插入一个结点y(该结点也可以是c,如图①),即y可以是c,也可以是c的右孩子(如图②),也可以是c的左孩子(不在画出)
实现:找到根结点x,与它的右孩子a进行交换即可使二叉树树再次平衡;
左右:
即在x的左孩子a的右孩子c上插入一个结点y(该结点也可以是c,如图①),即y可以是c,也可以是c的右孩子(如图②),也可以是c的左孩子(不在画出)
这个左右和下边的右左,稍微复杂了点,需要进行两次交换,才能达到平衡,注意这时y是c的右孩子,最终y作为x的左孩子;若y是c的左孩子,最终y作为a
的右孩子,画图分析一下~~下边类似,不再敖述。
实现:找到根结点x,让x的左孩子a与x的左孩子a的右孩子c进行交换,然后再让x与x此时的左孩子c进行交换,最终达到平衡;
右左:
即在x的右孩子a的左孩子c上插入一个结点y(该结点也可以是c,如图①),即y可以是c,也可以是c的右孩子(如图②),也可以是c的左孩子(不在画出)
实现:找到根结点x,让x的右孩子a与x的右孩子a的左孩子c进行交换,然后再让x与x此时的右孩子c进行交换,最终达到平衡;
左左:前a->bf=1 后 x->bf=0,a->bf=0;右右:前a->bf=-1 后x->bf=0,a->bf=0;显然左左与右右的x与a变换后的bf都为0;
左右、右左中结点bf的改变要根据之前c的bf!
左右:若c->bf=1,则x->bf=-1,a->bf=0,c->bf=0;若c->bf=-1,则x->bf=0,a->bf=1,c->bf=0;若c->bf=0,则x->bf=0,a->bf=0,c->bf=0;
右左:若c->bf=1,则x->bf=0,a->bf=-1,c->bf=0;若c->bf=-1,则x->bf=1,a->bf=0,c->bf=0;若c->bf=0,则x->bf=0,a->bf=0,c->bf=0;
可以发现,当左右、右左的c->bf相同时x与a的bf刚好取相反的值。