zoukankan      html  css  js  c++  java
  • 平衡二叉树

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

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

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


  • 相关阅读:
    linux日常。
    tp5中的config类和config助手函数
    TP5隐藏index.php
    TP5读取数据概述
    TP5的安装部署概要
    eclipse4.7中文包安装方法。
    利用mysqldump备份magento数据库
    MySQL 基础知识
    PHP 基础知识
    妖怪与和尚过河问题
  • 原文地址:https://www.cnblogs.com/hjch0708/p/7554816.html
Copyright © 2011-2022 走看看