平衡二叉树这里停留了很久,费了很大功夫才弄懂。做个笔记比较好。
参考以下两位大佬的博客写的:
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 ",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刚好取相反的值。