平衡二叉树

平衡二叉树这里停留了很久,费了很大功夫才弄懂。做个笔记比较好。

参考以下两位大佬的博客写的:

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刚好取相反的值。


原文地址:https://www.cnblogs.com/hjch0708/p/7554816.html