• C平衡二叉树(AVL)创建和删除


      AVL是最先发明的自平衡二叉查找树算法。在AVL中任何节点的两个儿子子树的高度最大差别为一,所以它也被称为高度平衡树,n个结点的AVL树最大深度约1.44log2n。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。

      定义

      用LH,EH,RH分别表示左子树高,等高,右子树高,即平衡因子1、0、-1

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #define LH 1    // 左高
    #define EH 0    // 等高
    #define RH -1    // 右高
    
    typedef struct TreeNode{
        int data;
        int bf;
        struct TreeNode *left, *right;
    }TreeNode;

      旋转处理

      左旋和右旋,记住“左逆右顺”就可以

    /************************************************
     * 对以*p为根的二叉排序树作右旋处理,处理之后p指向新的树根结点,
     *     A                  B
     *    /                  / 
     *   B     旋转后变为     C   A
     *  /                     /
     * C   D                  D 
     * 即旋转处理之前的左子树的结点。
     ************************************************/
    void r_rotate(TreeNode **p){
        TreeNode *l = (*p)->left;
        (*p)->left = l->right;
        l->right = (*p);
        *p = l;
    }
    /************************************************
     * 对以*p为根的二叉排序树作左旋处理,处理之后p指向新的树根结点,
     * A                      B
     *                      / 
     *   B     旋转后变为     A   D
     *  /                     
     * C   D                  C   
     * 即旋转处理之前的右子树的结点。
     ************************************************/
    void l_rotate(TreeNode **p){
        TreeNode *r = (*p)->right;
        (*p)->right = r->left;
        r->left = (*p);
        *p = r;
    }

      左平衡处理

    所谓左平衡处理,就是某一根结点的左子树比右子树高,从而失去了平衡。

    (1)插入时如果需要左平衡处理,根结点左子树根平衡因子只可能为LH和RH。

    (2)删除和插入不同,根结点左子树根的平衡因子三种情况都可能出现,因为是删除根结点右子树中的结点从而引起左子树过高,在删除前,根结点左子树根的平衡因子是可以为EH的,此种情况同样是对根结点做简单右旋处理。  

    /************************************************
     * 对*t所指结点为根的二叉树作左平衡处理
     ************************************************/
    void left_balance(TreeNode **t){
        TreeNode *l, *lr;
        l = (*t)->left;
        switch(l->bf){
            case LH:
                (*t)->bf = l->bf = EH;
                r_rotate(t);
                break;
            case RH:
                lr = l->right;
                switch(lr->bf){
                    case LH:
                        (*t)->bf = RH;
                        l->bf = EH;
                        break;
                    case RH:
                        (*t)->bf = EH;
                        l->bf = LH;
                        break;
                    case EH:
                        (*t)->bf = l->bf = EH;
                        break;
                }
                lr->bf = EH;
                l_rotate(&(*t)->left);
                r_rotate(t);
                break;
            case EH:            // 删除节点时用到
                (*t)->bf = LH;
                l->bf = RH;
                r_rotate(t);
                break;
        }
    }

      右平衡处理

    类似左平衡处理,所谓右平衡处理,就是某一根结点的右子树比左子树高,从而失去了平衡。

    (1)插入时如果需要右平衡处理,根结点右子树根平衡因子只可能为LH和RH。

    (2)删除和插入不同,根结点右子树根的平衡因子三种情况都可能出现,因为是删除根结点左子树中的结点从而引起右子树过高,在删除前,根结点右子树根的平衡因子是可以为EH的,此种情况同样是对根结点做简单左旋处理。

    /************************************************
     * 对*t所指结点为根的二叉树作右平衡处理
     ************************************************/
    void right_balance(TreeNode **t){
        TreeNode *r, *rl;
        r = (*t)->right;
        switch(r->bf){
            case RH:
                (*t)->bf = r->bf = EH;
                l_rotate(t);
                break;
            case LH:
                rl = r->left;
                switch(rl->bf){
                    case RH:
                        (*t)->bf = LH;
                        r->bf = EH;
                        break;
                    case LH:
                        (*t)->bf = EH;
                        r->bf = RH;
                        break;
                    case EH:
                        (*t)->bf = r->bf = EH;
                        break;
                }
                rl->bf = EH;
                r_rotate(&(*t)->right);
                l_rotate(t);
                break;
            case EH:            // 删除节点时用到
                (*t)->bf = RH;
                r->bf = LH;
                l_rotate(t);
                break;
        }
    }

      插入处理

    在插入一个元素时,总是插入在一个叶子结点上。采用递归插入,也就是不断搜索平衡二叉树,找到一个合适的插入点(相同关键字不插入)。插入后,引起的第一个不平衡的子树的根结点,一定是在查找路径上离该插入点最近的。

    /************************************************ 
     * 若在平衡的二叉排序树t中不存在和e有相同关键字的结点,则插入一个  
     * 数据元素为e的新结点,并返回true,否则返回false。若因插入而使二叉排序树  
     * 失去平衡,则作平衡旋转处理,布尔变量taller反映t长高与否 
     ************************************************/ 
    bool insertAVL(TreeNode **t,int e,bool *taller){
        if( ! *t ){
            *t = (TreeNode *)malloc(sizeof(TreeNode));
            (*t)->data = e;
            (*t)->left = (*t)->right = NULL;
            (*t)->bf = EH;
            *taller = true;
        }
        else{
            if( e == (*t)->data ){
                *taller = false;
                return false;
            }
            if( e < (*t)->data ){    // 在左子树中查找插入点 
                if( ! insertAVL(&(*t)->left,e,taller)){    // 左子树插入失败 
                    return false;
                }
                if( *taller ){        // 左子树插入成功,且树增高 
                    switch( (*t)->bf ){
                        case LH:            // 原来t的左子树高于右子树
                            left_balance(t);// 左平衡处理
                            *taller = false;
                            break;
                        case EH:            // 原来t的左子树和右子树等高 
                            (*t)->bf = LH;    // 现在左子树高
                            *taller = true;    // 整棵树增高
                            break;
                        case RH:            // 原来t的右子树高
                            (*t)->bf = EH;    // 现在等高
                            *taller = false;// 树未增高
                            break;
                    }
                }
            }
            else{                    // 在右子树中查找插入点
                if( ! insertAVL(&(*t)->right,e,taller)){ // 右子树插入失败 
                    return false;
                }
                if( *taller ){                 // 右子树插入成功,且树增高 
                    switch( (*t)->bf ){
                        case RH:             // 原来t的右子树高
                            right_balance(t);// 右平衡处理
                            *taller = false;
                            break;
                        case EH:             // 原来t的左子树和右子树等高 
                            (*t)->bf = RH;     // 现在右子树高
                            *taller = true;
                            break;
                        case LH:             // 原来t的左子树高
                            (*t)->bf = EH;     // 现在等高
                            *taller = false;
                            break;
                    }
                }
            }            
        }
        return true;
    }

      删除处理

    删除和插入不同的是,删除的结点不一定是叶子结点,可能是树中的任何一个结点。

    在操作二叉查找树时,我们知道删除的结点可能有三种情况:(1)为叶子结点(2)左子树或右子树有一个为空(3)左右子树都不空。

    对第三种情况的处理这里我们采用删除前驱的方式。递归删除,判断删除后树是否“变矮”了,然后进行相应的处理。对(1)(2)中情况,很好处理,树的确是“变矮”了。对于第(3)种情况,我们不能直接找到前驱结点,然后把数据拷贝到原本要删除的根结点,最后直接删除前驱结点。因为这么做,我们无法判断原先根结点子树高度的变化情况。所以我们在找到前驱结点后,不是直接删除,而是采用在根结点左子树中递归删除前驱的方式。

    /************************************************ 
     * 若在平衡的二叉排序树t中存在和e有相同关键字的结点,则删除  
     * 并返回true,否则返回false。若因删除而使二叉排序树  
     * 失去平衡,则作平衡旋转处理,布尔变量lower反映t变矮与否 
     ************************************************/
    bool deleteAVL(TreeNode **t,int key,bool *lower){
        if( ! *t ) return false;
        TreeNode *q = NULL;
        if( key == (*t)->data ){
            if( NULL == (*t)->left ){        // 左子树为空,直接连接右节点
                q = (*t);
                (*t) = q->right;
                free(q);
                *lower = true;
            }
            else if( NULL == (*t)->right ){    // 右子树为空,直接连接左节点
                q = (*t);
                (*t) = q->left;
                free(q);
                *lower = true;
            }
            else{
                q = (*t)->left;
                while( q->right ){
                    q = q->right;
                }
                (*t)->data = q->data;
                deleteAVL( &(*t)->left,q->data,lower);    // 在左子树中递归删除前驱节点
            }
        }
        else if( key < (*t)->data ){
            if( !deleteAVL( &(*t)->left,key,lower) ){
                return false;
            }
            if( *lower ){
                switch( (*t)->bf ){
                    case LH:
                        (*t)->bf = EH;
                        *lower = true;
                        break;
                    case EH:
                        (*t)->bf = RH;
                        *lower = false;
                        break;
                    case RH:
                        right_balance(t);
                        if( EH == (*t)->right->bf ){
                            *lower = false;
                        }
                        else{
                            *lower = true;
                        }
                        
                        break;
                }
            }
        }
        else{
            if( !deleteAVL( &(*t)->right,key,lower) ){
                return false;
            }
            if( *lower ){
                switch( (*t)->bf ){
                    case RH:
                        (*t)->bf = EH;
                        *lower = true;
                        break;
                    case EH:
                        (*t)->bf = LH;
                        *lower = false;
                        break;
                    case LH:
                        left_balance(t);
                        if( EH == (*t)->left->bf ){
                            *lower = false;
                        }
                        else{
                            *lower = true;
                        }
                        
                        break;
                }
            }
        }
        return true;
    }

      遍历和查找

    /************************************************
     * 在*t所指平衡二叉树中递归查找等于key的数据元素,
     * 若查找成功,则返回true 
     ************************************************/
    bool searchAVL(TreeNode *t,int key,TreeNode *f,TreeNode **p){
        if( !t ){
            *p = f;
            return false;
        }
        else if( key == t->data ){
            *p = t;
            return true;
        }
        else if( key < t->data ){
            return searchAVL( t->left,key,t,p);
        }
        else{
            return searchAVL( t->right,key,t,p);
        }
    }
    /************************************************
     * 前序遍历
     ************************************************/
    void PreOrderTraverse(TreeNode *t){
        //printf("in PreOrderTraverse
    ");
        if( NULL == t ) return;
        printf("%2d ",t->data);
        PreOrderTraverse(t->left);
        PreOrderTraverse(t->right);
    }
    /************************************************
     * 前序遍历平衡因子
     ************************************************/
    void PreOrderTraverse_bf(TreeNode *t){
        //printf("in PreOrderTraverse
    ");
        if( NULL == t ) return;
        printf("%2d ",t->bf);
        PreOrderTraverse_bf(t->left);
        PreOrderTraverse_bf(t->right);
    }
    /************************************************
     * 中序遍历
     ************************************************/
    void InOrderTraverse(TreeNode *t){
        if( NULL == t ) return;    
        InOrderTraverse(t->left);
        printf("%2d ",t->data);
        InOrderTraverse(t->right);
    }
    /************************************************
     * 后序遍历
     ************************************************/
    void PostOrderTraverse(TreeNode *t){
        if( NULL == t ) return;    
        PostOrderTraverse(t->left);
        PostOrderTraverse(t->right);
        printf("%2d ",t->data);
    }

      测试代码和用例

    int main(){
        int i = 0, key = 1;
        int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -2, };
        TreeNode *t = NULL, *p = NULL;
        bool taller = false;
        bool lower = false;
        for( ; i < sizeof(arr)/sizeof(int); i++ ){
            insertAVL(&t,arr[i],&taller);
        }
        printf("value %d is%s in the tree
    ",key,( searchAVL(t,key,NULL,&p) ) ? "" : " not");
        key = 15;
        printf("value %d is%s in the tree
    ",key,( searchAVL(t,key,NULL,&p) ) ? "" : " not");
        printf("
    PreOrderTraverse:
    ");
        PreOrderTraverse(t);
        printf("
    PreOrderTraverse_bf:
    ");
        PreOrderTraverse_bf(t);
        printf("
    InOrderTraverse:
    ");
        InOrderTraverse(t);
        printf("
    after delete 3:
    ");
        deleteAVL( &t,3,&lower);
        PreOrderTraverse(t);
        printf("
    ");
    }

    value 1 is in the tree
    value 15 is not in the tree

    PreOrderTraverse:
    4 2 1 -2 3 8 6 5 7 9 10
    PreOrderTraverse_bf:
    0 1 1 0 0 0 0 0 0 -1 0
    InOrderTraverse:
    -2 1 2 3 4 5 6 7 8 9 10
    after delete 3:
    4 1 -2 2 8 6 5 7 9 10

  • 相关阅读:
    99%的人没使用过它俩,Docker最佳实践+2
    kubernetes 中 kubeconfig 的用法
    jenkins批量修改配置文件
    记一次K8s排错实战
    记一次失败记录: MindSpore1.3.0 GPU 源码安装 —— Ubuntu18.04系统 (最终安装结果为失败)
    【转载】 使用Python的ctypes查看内存
    (摘抄) 源码分析multiprocessing的Value Array共享内存原理
    (续) python 中 ctypes 的使用尝试
    python 中 ctypes 的使用尝试
    深度强化学习算法(深度强化学习框架)为考虑可以快速适用多种深度学习框架建议采用弱耦合的软件设计方法——快速适用于多种深度学习计算框架的深度强化学习框架设计方案
  • 原文地址:https://www.cnblogs.com/wbjxxzx/p/4703082.html
Copyright © 2020-2023  润新知