平衡二叉树(AVL)的创建、插入、删除

一、概念

 平衡二叉树(下文称AVL)是满足每个结点的左右子树高度(亦称深度)之差在{0,1,-1}这个范围内的二叉排序树(二叉排序树也叫二叉搜索树)。

AVL的优势是在查找时避免了出现一般二叉排序树查找的最坏情况。但是AVL在经过插入或删除结点的操作之后可能出现不平衡的情况,所以每次插入或删除完之后要判断是否平衡,不平衡就需要rebalance.这会增加插入删除的复杂度。

二、AVL的创建

正常的有规律的创建方法(实际上是各种资料采用的方法)是将创建,
看成从NULL开始,一次次对原有的AVL的插入(显然只有一个结点的Tree一定是AVL)。

三、AVL的插入
1.方法:

先把结点按二叉排序树的结构正常插入,再由底向上逐级判断是否平衡,不平衡就rebalance. 插一次可能经过几次rebalance。(具体情况请往下读)

2.插入处的叶子节点的情况:
《平衡二叉树(AVL)的创建、插入、删除》

                                 图1

显然

  1) 插入处的叶子有且仅有这4种情况
  2)对于②④来说,一定不影响原有树平衡
  3)对于①③来说,不确定是否影响原有树的平衡;如果影响,不平衡点一定在结点A的祖先结点中(包括父亲节点,下文同)。
  4)对于①③来说,我们要把这种不平衡的可能逐级传递给A的祖先结点,再逐级判断A的祖先节点 a.是否不平衡,不平衡需要rebalance, 
    之后b.是否有影响上一级平衡性的可能,有就需要继续传递这种可能。进行递归

3.不平衡节点的情况

!!注意:这里讨论的是三、2.4)中逐级往上,第一个出现不平衡的节点的情况。其他节点若不平衡都是依次逐级递归调整,讨论且只有讨论第一个出现的不平衡的节点的情况才有意义。

(1)插入前
《平衡二叉树(AVL)的创建、插入、删除》

                               图2
注:
LH:左子树高  
RH:右子树高 
EQ:左右子树等高
x:深度值

显然,

1)插入后第一个出现不平衡的节点在插入前有且仅有这两种情况
2)我们只需讨论其中一种情况,另一种同理可解

本文选择第一种,下文即根据第一种而展开

(2)插入后
《平衡二叉树(AVL)的创建、插入、删除》

                              图3
注:
DISEQ_LH:因为左子树过高而不平衡
DISEQ_RH:因为右子树过高而不平衡

显然

1)第一个出现不平衡的节点有且仅有这两种情况
2)有的书籍中将第一种情况称为”LL型“,第二种情况称为"LR"型

(3)对于”LL型“的讨论
《平衡二叉树(AVL)的创建、插入、删除》

                              图4

显然,

1)LL型有且仅有这两种情况
2)有的书籍将第一种情况称为"LL的一般型”,第二种称为“LL的简单型”,这很明显是不科学的,甚至是错误的叫法,
  因为一般型不能包含简单型还叫一般吗?本文将第一种称为”LL的次简型“,第二种称为“LL的最简型”。
3)rebalance方法如下图(二者代码一致):

《平衡二叉树(AVL)的创建、插入、删除》

                             图5

(3)对于”LR型“的讨论
《平衡二叉树(AVL)的创建、插入、删除》

                           图6

显然

1)"LR型"有且仅有这两种情况
2)本文将第一种称为”LR的次简型“,第二种称为“LR的最简型”
3)rebalance方法如下图(二者代码不一致,主要在于平衡因子的改变不同):

《平衡二叉树(AVL)的创建、插入、删除》

                           图7

四、AVL的删除
1.方法:

1)删除:先找到要删除的结点;然后判断结点的子树情况,如果无子树,直接删除,如果有一个孩子,删除后将孩子结点接到父亲结点上,
 如果有两个孩子,可以选择a.在左孩子中找到最大结点 b.在右孩子中找到最小结点 ,将找到的节点与要删除的结点的data替换,
 然后问题转化为在删除结点的左子树中删除找到的结点。变成递归。
2)出现不平衡,即进行rebalance,删除一次可能rebalance多次

2.删除处的叶子节点的情况
!!注意,此处指的是四、1.1)中递归到最后删除叶子结点的情况
《平衡二叉树(AVL)的创建、插入、删除》

                           图8

显然,

1)删除处的叶子有且仅有这4种情况
2)对于②④来说,一定不影响原有树平衡
3)对于①③来说,不确定是否影响原有树的平衡;如果影响,不平衡点一定在结点A的祖先结点中(包括父亲节点,下文同)。
 a.是否不平衡,不平衡需要rebalance, 之后b.是否有影响上一级平衡性的可能,有就需要继续传递这种可能。进行递归。

3.不平衡节点的情况
!!注意:这里讨论的是三、2.4)中逐级往上,第一个出现不平衡的节点的情况。其他节点若不平衡都是依次逐级递归调整,讨论且只有讨论第一个出现的不平衡的节点的情况才有意义。
(1)删除前
《平衡二叉树(AVL)的创建、插入、删除》

                                 图9

显然,
1)删除后第一个出现不平衡的节点在插入前有且仅有这两种情况
2)我们只需讨论其中一种情况,另一种同理可解(本文选择第一种,下文即针对第一种情况而展开

(2)删除后
《平衡二叉树(AVL)的创建、插入、删除》

                             图10

显然,

1)第一个出现不平衡的节点有且仅有这3种情况
2)对照前面的插入操作,我们知道,第一种不平衡情况称为”LL型“,第二种情况称为"LR"型,那么第三种呢?有的教材将其归为前两种之一,
那明显是错误的,请那么做的作者去看LL、LR型的定义。有的教材更差劲,甚至不讲AVL删除操作。我将第三种情况命名为“LE型”。

(3)对于”LL型“的讨论

操作同插入时的“LL型”,但请清楚具体情况与插入时不同

(4)对于”LR型“的讨论
《平衡二叉树(AVL)的创建、插入、删除》

                           图11

(5)对于”LE型“的讨论
《平衡二叉树(AVL)的创建、插入、删除》

                           图12
注:对于”LE型“,可以用"LL型"的操作旋转节点,也可以用"LR型“的操作旋转节点。(但注意过后平衡因子的改变与LL或LR不同),应用LL显然更简单。

五、关键源代码

enum BalanceFactor //平衡因子取值
    { 
        DISEQ_RH = -2, //因为右子树高2不平衡
        RH = -1, //右子树比左子树高1
        EQ = 0,  //左右子树等高
        LH =1,   //左子树比右子树高1
        DISEQ_LH = 2 //因为左子树高2不平衡
};

typedef struct Tnode
{
     BalanceFactor t_balancefacor;
     float t_data;
     struct  Tnode* lchild;
     struct  Tnode* rchild;
}Tnode;

void createBBST(Tnode* &tree);
bool searchBBST(Tnode* tree, float search_data);
bool insertBBST(Tnode* &tree, float insert_data, bool &taller);
bool deleteBBST(Tnode* &tree, float delete_data, bool &shorter);
/*注意:AVL的插入和删除是有固定方法的,虽然还有其他手段也能使得插入后的AVL树平衡,但不是我们要探讨的*/

//(1)插入:按二叉搜索树的方法插入,再调平衡。
// 此种插入方法一共有四种失衡状态:LL,LR,RR,RL

//(2)删除:使用其按顺序的前导或顺序后继节点替换将要删除的节点,然后删除按顺序的前导或顺序后继节点。
// 此种删除方法就是在其左子树中找最大节点,或在右子树中找最小节点替换,(再次注意,两种方式得到的结果不同)
// 然后再删除左子树中的最大节点或右子树中的最小节点,
// 然后从第一个不平衡点开始,调平衡,找不平衡点,调平衡,构成递归。
// 以下提到的祖先节点包括父节点

void createBBST(Tnode* &tree)
{
    printf("Please input a sequence of numbers for create a BBST (numbers are separated by blanks and the sequence ends with #.):\n");
    float insert_data;
    char buffer;
    scanf("%f", &insert_data);
    scanf("%c", &buffer);
    //printf("%g ", insert_data);
    bool taller = false;
    while (buffer != '#'){
        insertBBST(tree, insert_data,taller);
        scanf("%f", &insert_data);
        scanf("%c", &buffer);
        //printf("%g ", insert_data);
    }
}

bool searchBBST(Tnode* tree,float search_data)
//tree中找search_data, 找到返回true
{
    if (tree == NULL)
    {
        return false;
    }
    else
    {
        if (search_data < tree->t_data)
        {
            return searchBBST(tree->lchild, search_data);
        }
        else if (search_data > tree->t_data)
        {
            return searchBBST(tree->rchild, search_data);
        }
        else  
            return true;
    }
    return true;
}
Tnode* AdjustRR(Tnode* anode)
{
    Tnode* bnode = anode->rchild;
    anode->rchild = bnode->lchild;
    bnode->lchild = anode;

    anode->t_balancefacor = EQ;
    bnode->t_balancefacor = EQ;
    return bnode;
}

Tnode* AdjustLL(Tnode* anode)
{
    Tnode* bnode = anode->lchild;
    anode->lchild = bnode->rchild;
    bnode->rchild = anode;

    anode->t_balancefacor = EQ;
    bnode->t_balancefacor = EQ;
    return bnode;
}
Tnode* AdjustRL(Tnode* anode)
{
    Tnode* bnode = anode->rchild;
    Tnode* cnode = bnode->lchild;
    anode->rchild = cnode->lchild;
    bnode->lchild = cnode->rchild;
    cnode->lchild = anode;
    cnode->rchild = bnode;
    if (cnode->t_balancefacor == EQ) //最简单不平衡情况
    {
        anode->t_balancefacor = EQ;
        bnode->t_balancefacor = EQ;
        cnode->t_balancefacor = EQ;
    }
    else//次简单不平衡情况
    {
        if (cnode->t_balancefacor == LH)
        {
            anode->t_balancefacor = EQ;
            bnode->t_balancefacor = RH;
            cnode->t_balancefacor = EQ;
        }
        else if (cnode->t_balancefacor == RH)
        {
            anode->t_balancefacor = LH;
            bnode->t_balancefacor = EQ;
            cnode->t_balancefacor = EQ;
        }

    }

    return cnode;
}
Tnode* AdjustLR(Tnode* anode)
{
    Tnode* bnode = anode->lchild;
    Tnode* cnode = bnode->rchild;
    anode->lchild = cnode->rchild;
    bnode->rchild = cnode->lchild;
    cnode->lchild = bnode;
    cnode->rchild = anode;

    if (cnode->t_balancefacor == EQ)  //处理最简单不平衡情况
    {
        anode->t_balancefacor = EQ;
        bnode->t_balancefacor = EQ;
        cnode->t_balancefacor = EQ;
    }
    else//处理次简单不平衡情况
    {
        if (cnode->t_balancefacor == LH)
        {
            anode->t_balancefacor = RH;
            bnode->t_balancefacor = EQ;
            cnode->t_balancefacor = EQ;
        }
        else if (cnode->t_balancefacor == RH)
        {
            anode->t_balancefacor = EQ;
            bnode->t_balancefacor = LH;
            cnode->t_balancefacor = EQ;
        }
    }
    return cnode;
}

bool insertBBST(Tnode* &tree, float insert_data, bool &taller)
//在tree中插入inser_data, 成功插入返回true
{
    taller = false;  //多赋值,可能是冗余操作,后单步知并不冗余
    bool inserted = false;
    if (tree == NULL)
    {
        tree = (Tnode*)malloc(sizeof(Tnode));
        tree->t_data = insert_data;
        tree->lchild = tree->rchild = NULL;
        tree->t_balancefacor = EQ;
        taller = true;
        inserted = true;
        return inserted;
        //return true;
    }
    else
    {
        if (insert_data < tree->t_data)
        {
            inserted = insertBBST(tree->lchild, insert_data, taller);

            //最下面的节点
            if (taller == true)
            {
                switch (tree->t_balancefacor)//判断在插入之前tree->t_balancefactor的值
                {
                case EQ:
                    tree->t_balancefacor = LH;
                    taller = true; //递归给上一级的taller
                    break;

                case LH:  //走到此分支均是因为增加节点之后,不平衡。此处进行rebalance,并重置balancefactor

                    tree->t_balancefacor = DISEQ_LH;
                    //可能出现LR,也可能LL
                    //只能是这2种情况,因为前面有if(taller)限制,tree->lchild->balancefactor此刻就不能是EQ
                    //换一种有趣的说法是不能一次加两个节点,所以tree->lchild->balancefactor此刻不能是EQ
                    if (tree->lchild->t_balancefacor == RH)
                    {
                        /*LR类型处理并修改平衡因子*/
                        tree = AdjustLR(tree);
                        taller = false;
                        break;
                    }
                    else if (tree->lchild->t_balancefacor == LH)
                    {
                        /*LL类型处理并修改平衡因子*/
                        tree = AdjustLL(tree);
                        taller = false;
                        break;
                    }

                case RH:
                    tree->t_balancefacor = EQ;
                    taller = false;
                    break;
                }
            }
            return inserted;
        }
        else if (insert_data > tree->t_data)
        {
            inserted = insertBBST(tree->rchild, insert_data, taller);

            if (taller == true)
            {
                switch (tree->t_balancefacor)//判断在插入之前tree->t_balancefactor的值
                {
                case EQ:
                    tree->t_balancefacor = RH;
                    taller = true; //递归给上一级的taller
                    break;

                case LH:
                    tree->t_balancefacor = EQ;
                    taller = false;
                    break;

                case RH:  //走到此分支均是因为增加节点之后,不平衡。此处进行rebalance,并重置balancefactor
                    tree->t_balancefacor = DISEQ_RH;

                    //可能出现LR,也可能LL
                    //只能是这2种情况,因为前面有if(taller)限制,tree->rchild->balancefactor此刻就不能是EQ
                    //换一种有趣的说法是不能一次加两个节点,所以tree->lchild->balancefactor此刻不能是EQ
                    if (tree->rchild->t_balancefacor == LH)
                    {
                        /*RL处理并修改平衡因子*/
                        tree = AdjustRL(tree);
                        taller = false;
                        break;
                    }
                    else  if (tree->rchild->t_balancefacor == RH)
                    {   

                        /*RR处理并修改平衡因子*/
                        tree = AdjustRR(tree);
                        taller = false; 
                        break;
                    }   
                }
            }
            return inserted;
        }
        else
        {
            return false;   //insert_data已存在,返回false
        }
    }
}


//虽然删除节点与插入的调整LL\LR\RR\RL的具体情况不一样,但是居然得到正确结果的操作并无不同
Tnode* AdjustLL_delete(Tnode* anode)
{
    return AdjustLL(anode);
}
Tnode* AdjustRR_delete(Tnode* anode)
{
    return AdjustRR(anode);
}
Tnode* AdjustLR_delete(Tnode* anode)
{
    return AdjustLR(anode);
}
Tnode* AdjustRL_delete(Tnode* anode)
{
    return AdjustRL(anode);
}
Tnode* AdjustLE_delete(Tnode* anode) //anode->balancefator == LH
{
    Tnode* bnode = anode->lchild;
    anode->lchild = bnode->rchild;
    bnode->rchild = anode;

    anode->t_balancefacor = LH;
    bnode->t_balancefacor = RH;
    return bnode;
}
Tnode* AdjustRE_delete(Tnode* anode) //anode->balancefator == RH
{
    Tnode* bnode = anode->rchild;
    anode->rchild = bnode->lchild;
    bnode->lchild = anode;

    anode->t_balancefacor = RH;
    bnode->t_balancefacor = LH;
    return bnode;
}

/******************************************************************************************* 1.删除 (1)Deleting a node with 0 children nodes 直接删除 (2)Deleting a node with 1 children nodes 将另一个子树接上 (3)Deleting a node with 2 children nodes : Replace the(to - delete) node with its in - order predecessor or in - order successor Then delete the in - order predecessor or in - order successor 2.调平衡 The actionPos (action position) in a delete operation is the parent node of the "deleted" node 不平衡点可能且只可能是被删除的节点的祖先节点 *********************************************************************************************/
bool deleteBBST(Tnode* &tree, float delete_data, bool& shorter)
{

    shorter = false;
    bool deleted = false;
    if (tree == NULL)
    {
        return false; //删除失败
    }
    else if(delete_data < tree->t_data)
    {
        deleted = deleteBBST(tree->lchild, delete_data, shorter);

        if (shorter)
        {
            switch (tree->t_balancefacor) //由原来祖先节点的balancefactor修改删除操作后现在祖先节点的balancefactor
            {
            case EQ:
                tree->t_balancefacor = RH;
                shorter = false;
                return deleted;
            case LH:
                tree->t_balancefacor = EQ;
                shorter = true;
                return deleted;
            case RH:   //走到此分支均是因为删除节点之后,不平衡。此处进行rebalance,并重置balancefactor

                if ( tree->rchild->t_balancefacor == LH)
                {
                    //delete 的 RL处理并修改平衡因子
                    tree = AdjustRL_delete(tree);
                    shorter = true;
                    return deleted;
                }
                else if (tree->rchild->t_balancefacor == RH)
                {
                    //delete 的 RR处理并修改平衡因子
                    tree = AdjustRR_delete(tree);
                    shorter = true;
                    return deleted;

                }
                //比增加节点rebalance多此情况,是因为删除一个节点可能等效于使得另一分支增加了多个节点
                else if (tree->rchild->t_balancefacor == EQ)
                {
                    //delete 的 RE处理并修改平衡因子
                    tree = AdjustRE_delete(tree);
                    shorter = false;
                    return deleted;
                }
            }
        }
    }
    else if (delete_data > tree->t_data)
    {
        deleted = deleteBBST(tree->rchild, delete_data, shorter);

        if (shorter)
        {
            switch (tree->t_balancefacor)//由原来祖先节点的balancefactor修改删除操作后现在祖先节点的balancefactor
            {
            case EQ:
                tree->t_balancefacor = LH;
                shorter = false;
                return deleted;
            case RH:
                tree->t_balancefacor = EQ;
                shorter = true;
                return deleted;
            case LH:  //走到此分支均是因为删除节点之后,不平衡。此处进行rebalance,并重置balancefactor

                if (tree->lchild->t_balancefacor == LH)
                {
                    //delete 的 LL处理并修改平衡因子
                    tree = AdjustLL_delete(tree);
                    shorter = true;
                    return deleted;
                }
                else if (tree->lchild->t_balancefacor == RH)
                {
                    //delete 的 LR处理并修改平衡因子
                    tree = AdjustLR_delete(tree);
                    shorter = true;
                    return deleted;
                }
                else if (tree->lchild->t_balancefacor == EQ)
                {
                    //delete 的 LE处理并修改平衡因子
                    tree = AdjustLE_delete(tree);
                    shorter = false;
                    return deleted;
                }
            }
        }
    }

    else if (delete_data == tree->t_data)
    {
        //下面的前三个if为最简单操作,最后一种情况递归到上面if(shorter)里
        // if(tree->lchild == tree->rchild == NULL) 会出现逻辑错误

        if (tree->lchild == NULL && tree->rchild == NULL)
        {
            tree = NULL;
            shorter = true;
            deleted = true;
            return deleted;
        }
        else if ((tree->lchild != NULL) && (tree->rchild == NULL))
        {
            tree = tree->lchild;
            shorter = true;
            deleted = true;
            return deleted;
        }
        else if ((tree->rchild != NULL) && (tree ->lchild == NULL))
        {
            tree = tree->rchild;
            shorter = true;
            deleted = true;
            return deleted;
        }
        else //被删除的节点即存在左子树也存在右子树
        {
            Tnode* inorder_predecessor = tree->lchild; //用来记录顺序前导节点
            while (inorder_predecessor->rchild != NULL)
            {
                inorder_predecessor = inorder_predecessor->rchild;
            }
            tree->t_data = inorder_predecessor ->t_data;  //替换要删除的节点
            //原问题转化为在tree->lchild中删除inorder_predecessor指向的节点
            deleted = deleteBBST(tree->lchild, inorder_predecessor->t_data, shorter);

            if (shorter)
            {
                switch (tree->t_balancefacor) //由原来祖先节点的balancefactor修改删除操作后现在祖先节点的balancefactor
                {
                case EQ:
                    tree->t_balancefacor = RH;
                    shorter = false;
                    return deleted;
                case LH:
                    tree->t_balancefacor = EQ;
                    shorter = true;
                    return deleted;
                case RH:   //走到此分支均是因为删除节点之后,不平衡。此处进行rebalance,并重置balancefactor
                    if (tree->rchild->t_balancefacor == LH)
                    {
                        //delete 的 RL处理并修改平衡因子
                        tree = AdjustRL_delete(tree);
                        shorter = true;
                        return deleted;
                    }
                    else if (tree->rchild->t_balancefacor == RH)
                    {
                        //delete 的 RR处理并修改平衡因子
                        tree = AdjustRR_delete(tree);
                        shorter = true;
                        return deleted;

                    }
                    //比增加节点rebalance多此情况,是因为删除一个节点可能等效于使得另一分支增加了多个节点
                    else if (tree->rchild->t_balancefacor == EQ)
                    {
                        //delete 的 RE处理并修改平衡因子
                        tree = AdjustRE_delete(tree);
                        shorter = false;
                        return deleted;
                    }
                }
            }
            return deleted;
        }
    }
}

参考:1
但请注意,这个资料里举例的图存在错误

    原文作者:平衡二叉树
    原文地址: https://blog.csdn.net/D20152107/article/details/77931827
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞