平衡二叉查找树---AVL树

二叉查找树:树的每个节点的值比左子树上所有节点都大比右子树上所有节点都小。

定义左子树与右子树的高度差即为该节点的平衡因子(BF, Balance Factor)

平衡二叉查找树(AVL树):BF绝对值小于等于1。平衡因子绝对值大于1则说明此树不是平衡二叉树,此时需要重新调整树的结构

节点重平衡

添加节点的时候,每当左右子树高度差的绝对值为2的时候,进行节点位置的调整。通过旋转使得左右子树的高度差减少。

右旋

旋转过程如下图所示

​

仅当左子树高度比右子树高度,才需要右旋,右旋使得左子树的高度减小,右子树到高度增加。

令A节点的平衡因子为Abf,令L节点的平衡因子为Lbf,子树a的bf值为abf,子树b的bf值为bbf,子树c的bf值为cbf,

左右子树关系变化

右旋时左右子树变化变换过程

①→②  A->lchild = L->rchild

②→③  L->rchild = A

平衡因子变换

变换过程中的平衡因子(BF,Balance Factor)变化,观察从①到③

① 中:Abf = max(abf, bbf) + 1 - cbf,Lbf = abf - bbf

③ 中:Lbf = abf - max(bbf, cbf) -1 ,Abf = bbf - cbf

变化结果如下表格(1):

 代码:

void RightSingleRotate(struct Node **avltree){
    int leftbalancefactor =  (*avltree)->lchild->balancefactor,treebalancefactor=(*avltree)->balancefactor;
    struct Node *temp = (*avltree)->lchild;
    (*avltree)->lchild = temp->rchild;
    temp->rchild = (*avltree);
    *avltree = temp;
    switch(treebalancefactor){
        case 2:
            switch(leftbalancefactor){
                case 2 :
                    (*avltree)->balancefactor = 0;
                    (*avltree)->rchild->balancefactor = -1;
                    break;
                case 1 :
                    (*avltree)->balancefactor = 0;
                    (*avltree)->rchild->balancefactor = 0;
                    break;
                case 0 :
                    (*avltree)->balancefactor = -1;
                    (*avltree)->rchild->balancefactor = 1;
                    break;
            }
            break;
        case 1:
            switch(leftbalancefactor){
                case 1 :
                    (*avltree)->balancefactor = -1;
                    (*avltree)->rchild->balancefactor = -1;
                    break;
                case 0 :
                    (*avltree)->balancefactor = -1;
                    (*avltree)->rchild->balancefactor = 0;
                    break;
            }
            break;
    }
}
View Code

左旋

旋转过程如下图所示 

 ​

仅当右子树高度比左子树高度,才需要左旋,左旋使得右子树的高度减小,左子树高度增加。

令A节点的平衡因子为Abf,令R节点的平衡因子为Rbf,子树a的bf值为abf,子树b的bf值为bbf,子树c的bf值为cbf,

左右子树关系变化

右旋时左右子树变化变换过程

①→②  A->rchild = R->lchild

②→③  R->lchild = A

平衡因子变换

变换过程中的平衡因子(BF,Balance Factor)变化,观察从①到③

① 中:Abf = abf - max(bbf, cbf) - 1 , Rbf = bbf - cbf

③ 中:Rbf = max(abf, bbf) - cbf + 1 , Abf = abf - bbf

变换如下列表(2):

相关代码

void LeftSingleRotate(struct Node **avltree){
    int rightbalancefactor =  (*avltree)->rchild->balancefactor,treebalancefactor=(*avltree)->balancefactor;
    struct Node *temp = (*avltree)->rchild;
    (*avltree)->rchild = temp->lchild;
    temp->lchild = (*avltree);
    *avltree = temp;
    switch(treebalancefactor){
        case -2:
            switch(rightbalancefactor){
                case -2 :
                    (*avltree)->balancefactor = 0;
                    (*avltree)->lchild->balancefactor = 1;
                    break;
                case -1 :
                    (*avltree)->balancefactor = 0;
                    (*avltree)->lchild->balancefactor = 0;
                    break;
                case 0 :
                    (*avltree)->balancefactor = 1;
                    (*avltree)->lchild->balancefactor = -1;
                    break;
            }
            break;
        case -1:
            switch(rightbalancefactor){
                case -1 :
                    (*avltree)->balancefactor = 1;
                    (*avltree)->lchild->balancefactor = 1;
                    break;
                case 0 :
                    (*avltree)->balancefactor = 1;
                    (*avltree)->lchild->balancefactor = 0;
                    break;
            }
            break;
    }
}
View Code

左子树旋转

如果节点A的平衡因子为2,则需要对左子树进行旋转。设当前节点为A,左孩子为L。

(1) Abf = 1,进行新增/减少节点

  • Lbf = 0,新增节点,使得a子树高度加1,说明在新增节点前有 abf = bbf = cbf
  • Lbf = 1,减少节点,使得c子树高度减1,说明在减少节点前有 abf - bbf = 1 , cbf = abf

  得到 Abf = 2,Lbf = 1

(2)若 Abf = 1,进行新增/减少节点

  • Lbf = -1,减少节点,使得c子树高度减1,说明在减少节点前有  bbf - abf = 1 , cbf = bbf
  • Lbf = 0,增加节点,b子树高度加1,说明在新增节点前有 abf = bbf = cbf

   得到 Abf = 2,Lbf = -1

(3)若 Abf = 1,进行新增/减少节点

  • Lbf = 0,减少节点,使得d子树高度减1,说明在减少节点前有 abf = bbf = cbf

   得到 Abf = 2,Lbf = -1

场景1

Abf = 1时候,若新增或删除节点后有Abf = 2,Lbf = 1

则根据旋转表格结果(1),旋转后有 Abf = 0,如果Lbf = 0,一次右旋即可保证结束调整

观察右旋的变化图形,再考虑高度,ha-hb=1,ha-hc=1,,①中树的高度是ha+2,③中树的高度变为ha+1,

  • 如果变换的原因是新增节点,那些新增节点之前,此子树的高度为ha+1,说明此树的高度不变
  • 如果变换的原因是减少节点,那么减少节点之前,此子树的高度为ha+2,说明此树的高度减1

 场景2

 

 Abf = 1时候,若新增或删除节点后有Abf = 2,Lbf = -1,

根据旋转表格结果(1)(2),旋转后有 Abf = 1,如果Lbf = -2,一次右旋无法解解决问题

因此可以对A的左节点L先进行左旋转,再对A进行右旋转,如下图所示:

①→③ :此时 Lbf = -1,若 LRbf ∈ {-1,0,1},对A的左子树进行左旋变换之后有 LRbf ∈  {1,2}

③→⑤ :相当于场景1中的Abf = 2,Lbf = 1,进行一次右旋即可解决问题,最终 LRbf = 0,Lbf ∈ {0,1},Abf ∈ {-1,0},

再考虑高度,ha=max(hb,hc),hd=ha,①中树的高度是ha+3,⑤中树的高度变为ha+2,

  • 如果变换的原因是新增节点,那些新增节点之前,此子树的高度为ha+2,说明此树的高度不变
  • 如果变换的原因是减少节点,那么减少节点之前,此子树的高度为ha+3,说明此树的高度减1

场景3

 Abf = 1时候,若删除节点后有Abf = 2,Lbf = 0,根据旋转表格结果(1),一次右旋即可解决问题

 

此时如果a节点的高度为h,b和c节点高度为h-1,变换之前根节点的高度为h+2,变换之后高度为h+1

再考虑高度,ha=hb,hc=ha-1,①中树的高度是ha+2,⑤中树的高度变为ha+1,

  • 此场景变换的原因只能是减少节点,那么减少节点之前,此子树的高度为ha+2,说明此树的高度减1

代码

void ReBalanceAVLLeftTree(struct Node **avltree){
    struct Node *L,*LR;
    L = (*avltree)->lchild;
    switch(L->balancefactor){
        case 1:
            RightSingleRotate(avltree);
            break;
        case -1:
            LeftSingleRotate(&(*avltree)->lchild);
            RightSingleRotate(avltree);
            break;
        case 0:
            RightSingleRotate(avltree);break;
    }
}
View Code

右子树旋转

逻辑同左子树旋转

代码

void ReBalanceAVLRightTree (struct Node **avltree) {
    struct Node *R,*RL;
    R = (*avltree)->rchild;switch(R->balancefactor){
        case -1:
            LeftSingleRotate(avltree);
            break;
        case 1:
            RightSingleRotate(&(*avltree)->rchild);
            LeftSingleRotate(avltree);
            break;
        case 0:
            LeftSingleRotate(avltree);break;
    }
}
View Code

节点增删

增加节点

对于二叉查找树,节点存储的数值等于搜索的值,那么树中已存在节点无需新增,节点存储的数值大于搜索的值,在左子树进行下一步搜索,节点存储的数值小于搜索的值,在右子树进行下一步搜索。

递归查找新增节点的位置,每次新增节点成功,则返回插入成功,并告诉上一层,是否需要修改上层的平衡因子数值。

对于添加成功的位置,因为对于上层,添加位置的左/右节点本来没有数值,相当于左或右子树至少有一个为空。对于上层节点,则添加节点后,平衡因子必然会发现变化。不断向上传递平衡因子的变化。

同时,在上面的左子树旋转的讨论中可以知道,当节点增加导致重新平衡时,树的高度不变,高度没有增长,同时将高度未增长的信息传递给父节点,用变量grow 表示。

左子树需要做的检查的内容,代码示例如下:

switch((*avltree)->balancefactor){
    case 1:
        (*avltree)->balancefactor = 2;
        ReBalanceAVLLeftTree(avltree);
        *grow = false;
        break;
    case 0:
        (*avltree)->balancefactor = 1;*grow = true;
        break;
    case -1:
        (*avltree)->balancefactor = 0;*grow = false;
        break;
}
View Code

右子树需要做的检查的内容,代码示例如下:

switch((*avltree)->balancefactor){
    case -1:
        (*avltree)->balancefactor = -2;
        ReBalanceAVLRightTree(avltree);
        *grow = false;
        break;
    case 0:
        (*avltree)->balancefactor = -1;*grow = true;
        break;
    case 1:
        (*avltree)->balancefactor = 0;*grow = false;
        break;
}
View Code

 新增节点代码如下

bool InsertEleIntoAVLTree(AVLNode **avltree,int insertkey,bool* grow){
    if(*avltree == NULL){
        申请空间新增节点
    }else{
        if((*avltree)->data == insertkey){
            *grow = false;
            return false;
        }else if ((*avltree)->data > insertkey){
            bool success = InsertEleIntoAVLTree(&(*avltree)->lchild,insertkey,grow);
            if(!success)
                return false;
            else if(*grow){
                左子树的平衡因子变化
            }
        }else{
            bool success = InsertEleIntoAVLTree(&(*avltree)->rchild,insertkey,grow);
            if(!success)
                return false;
            else if(*grow){
                右子树的平衡因子变化
            }
        }
    }
    return true;
}
View Code

删除节点

对于二叉查找树,递归查找删除节点的位置,若删除节点成功,则返回删除成功,并告诉上一层,是否需要修改上层的平衡因子数值,用变量degrow 表示。不断向上传递平衡因子的变化。

在上面的左子树旋转的讨论中可以知道,当节点删除导致重新平衡时,树的高度必然会发生变化,即高度减小了,同时将高度减小的信息传递给父节点。

因此,左子树节点删除导致平衡因子变化需要做如下检查:

switch ((*avltree)->balancefactor)
{
    case 1:
        (*avltree)->balancefactor = 0;
        break;
    case 0:
        (*avltree)->balancefactor = -1;
        *degrow = false;
        break;
    case -1:
        *degrow = true;
        (*avltree)->balancefactor = -2;
        ReBalanceAVLRightTree(avltree);
        break;
}
View Code

右子树除节点导致平衡因子变化需要做如下检查:

switch ((*avltree)->balancefactor)
{
    case -1:
        (*avltree)->balancefactor = 0;
        break;
    case 0:
        (*avltree)->balancefactor = 1;
        *degrow = false;
        break;
    case 1:
        *degrow = true;
        (*avltree)->balancefactor = 2;
        ReBalanceAVLLeftTree(avltree);
        break;
}
View Code

删除节点本身可能需要重新链接节点和子树的关系,而且由于节点变化,平衡因子也要调整,导致节点和子树链接的变化

对于二叉查找树,节点存储的数值等于搜索的值,那么树中已存在节点无需新增,节点存储的数值大于搜索的值,在左子树进行下一步搜索,节点存储的数值小于搜索的值,在右子树进行下一步搜索。

(1)如果找到需要删除的节点,有左右子树,删除会导致左右子树变化

假设A节点为要删除的节点,L为A的左子树,R为A的右子树,对于二叉查找树,设L子树中最右边的节点为LA,可以LA的数值大于L子树中所有节点的数值,且小于R子树中所有节点的数值,因此,可以用LA替代A节点,但是如果直接删除LA节点,很难确定L子树的高度是否变化。

考虑添加节点时的操作,如果把LA的值赋给A节点,并从L子树搜索LA节点,找到LA之后,进行真正的删除。注意这个过程中,一开始要删除的键值为A->data,后续需要删除的键值LA->data ,如上图所示。

设置一个变量realdel,初始化为1,当找到要删除数值,则设置变量为0,然后从左子树开始搜索这个要删除的节点。realdel这个已经设置为0,且只会变化一次,不会反向变为0,这个过程只会执行一次。

代码示例如下:

if(*realdel == 1 && (*avltree)->lchild != NULL){
    int realdata = FindRealDeleteEleValue(*avltree,*deletekey);
    *deletekey = realdata;
    (*avltree)->data = realdata;
    *realdel = 0;
    bool success = DeleteEleFromAVLTreeRecu(&(*avltree)->lchild,deletekey,degrow,realdel);
    printf("Get %d\n");
    if(!success)
        return false;
    else if(*degrow) {
        *degrow = true;
     进行左子树节点删除导致平衡因子变化需要做如下检查
    }
}
View Code

函数FindRealDeleteEleValue代码示例

int FindRealDeleteEleValue(BstNode *avltree,int key){
    BstNode *L=avltree->lchild,*LR=L->rchild;
    while(LR!=NULL){
        L = LR;
        LR = LR->rchild;
    }
    int data = L->data;
    return data;
}
View Code

(2)执行删除,释放空间

代码如下:

BstNode *temp = *avltree;
if (*realdel == 0){
    if((*avltree)->lchild == NULL){
        *avltree = NULL;
    }else{
        *avltree = (*avltree)->lchild;
    }
}else if((*avltree)->lchild == NULL){
    *realdel = 0;
    *avltree = (*avltree)->rchild;
}
free(temp);
*degrow = true;
View Code

 

完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

typedef struct Node{
    int data;
    int balancefactor;
    struct Node *lchild,*rchild;
} AVLNode,BstNode;

void PreOrderTraverse(AVLNode * tree){
    if(tree!=NULL){
        printf("%d %d\n",tree->data,tree->balancefactor);
        PreOrderTraverse(tree->lchild);
        PreOrderTraverse(tree->rchild);
    }
}

void InOrderTraverse(struct Node * tree){
    if(tree!=NULL){
        InOrderTraverse(tree->lchild);
        printf("%d \n",tree->data);
        InOrderTraverse(tree->rchild);
    }
}

void RightSingleRotate(struct Node **avltree){
    int leftbalancefactor =  (*avltree)->lchild->balancefactor,treebalancefactor=(*avltree)->balancefactor;
    struct Node *temp = (*avltree)->lchild;
    (*avltree)->lchild = temp->rchild;
    temp->rchild = (*avltree);
    *avltree = temp;
    switch(treebalancefactor){
        case 2:
            switch(leftbalancefactor){
                case 2 :
                    (*avltree)->balancefactor = 0;
                    (*avltree)->rchild->balancefactor = -1;
                    break;
                case 1 :
                    (*avltree)->balancefactor = 0;
                    (*avltree)->rchild->balancefactor = 0;
                    break;
                case 0 :
                    (*avltree)->balancefactor = -1;
                    (*avltree)->rchild->balancefactor = 1;
                    break;
            }
            break;
        case 1:
            switch(leftbalancefactor){
                case 1 :
                    (*avltree)->balancefactor = -1;
                    (*avltree)->rchild->balancefactor = -1;
                    break;
                case 0 :
                    (*avltree)->balancefactor = -1;
                    (*avltree)->rchild->balancefactor = 0;
                    break;
            }
            break;
    }
}

void LeftSingleRotate(struct Node **avltree){
    int rightbalancefactor =  (*avltree)->rchild->balancefactor,treebalancefactor=(*avltree)->balancefactor;
    struct Node *temp = (*avltree)->rchild;
    (*avltree)->rchild = temp->lchild;
    temp->lchild = (*avltree);
    *avltree = temp;
    switch(treebalancefactor){
        case -2:
            switch(rightbalancefactor){
                case -2 :
                    (*avltree)->balancefactor = 0;
                    (*avltree)->lchild->balancefactor = 1;
                    break;
                case -1 :
                    (*avltree)->balancefactor = 0;
                    (*avltree)->lchild->balancefactor = 0;
                    break;
                case 0 :
                    (*avltree)->balancefactor = 1;
                    (*avltree)->lchild->balancefactor = -1;
                    break;
            }
            break;
        case -1:
            switch(rightbalancefactor){
                case -1 :
                    (*avltree)->balancefactor = 1;
                    (*avltree)->lchild->balancefactor = 1;
                    break;
                case 0 :
                    (*avltree)->balancefactor = 1;
                    (*avltree)->lchild->balancefactor = 0;
                    break;
            }
            break;
    }
}

void ReBalanceAVLRightTree (struct Node **avltree) {
    struct Node *R,*RL;
    R = (*avltree)->rchild;
    switch(R->balancefactor){
        case -1:
            LeftSingleRotate(avltree);
            break;
        case 1:
            RightSingleRotate(&(*avltree)->rchild);
            LeftSingleRotate(avltree);
            break;
        case 0:
            LeftSingleRotate(avltree);
            break;
    }
}

void ReBalanceAVLLeftTree(struct Node **avltree){
    struct Node *L,*LR;
    L = (*avltree)->lchild;
    switch(L->balancefactor){
        case 1:
            RightSingleRotate(avltree);
            break;
        case -1:
            LeftSingleRotate(&(*avltree)->lchild);
            RightSingleRotate(avltree);
            break;
        case 0:
            RightSingleRotate(avltree);
            break;
    }
}

int FindRealDeleteEleValue(BstNode *avltree,int key){
    BstNode *L=avltree->lchild,*LR=L->rchild;
    while(LR!=NULL){
        L = LR;
        LR = LR->rchild;
    }
    int data = L->data;
    return data;
}

bool DeleteEleFromAVLTreeRecu(BstNode **avltree,int* deletekey , bool *degrow,int *realdel){
    if(*avltree == NULL){
        return false;
    }else{
        if((*avltree)->data == *deletekey){
            if(*realdel == 1 && (*avltree)->lchild != NULL){
                int realdata = FindRealDeleteEleValue(*avltree,*deletekey);
                *deletekey = realdata;
                (*avltree)->data = realdata;
                *realdel = 0;
                bool success = DeleteEleFromAVLTreeRecu(&(*avltree)->lchild,deletekey,degrow,realdel);
                if(!success)
                    return false;
                else if(*degrow) {
                    *degrow = true;
                    switch ((*avltree)->balancefactor)
                    {
                        case 1:
                            (*avltree)->balancefactor = 0;
                            break;
                        case 0:
                            (*avltree)->balancefactor = -1;
                            *degrow = false;
                            break;
                        case -1:
                            *degrow = true;
                            (*avltree)->balancefactor = -2;
                            ReBalanceAVLRightTree(avltree);
                            break;
                    }
                }
            }else {
                BstNode *temp = *avltree;
                if (*realdel == 0){
                    if((*avltree)->lchild == NULL){
                        *avltree = NULL;
                    }else{
                        *avltree = (*avltree)->lchild;
                    }
                }else if((*avltree)->lchild == NULL){
                    *realdel = 0;
                    *avltree = (*avltree)->rchild;
                }
                free(temp);
                *degrow = true;
            }
        }else if ((*avltree)->data > *deletekey){
            bool sucess = DeleteEleFromAVLTreeRecu(&(*avltree)->lchild,deletekey,degrow,realdel);
            if(!sucess)
                return false;
            else if(*degrow){
                *degrow = true;
                switch ((*avltree)->balancefactor)
                {
                    case 1:
                        (*avltree)->balancefactor = 0;
                        break;
                    case 0:
                        (*avltree)->balancefactor = -1;
                        *degrow = true;
                        break;
                    case -1:
                        *degrow = true;
                        (*avltree)->balancefactor = -2;
                        ReBalanceAVLRightTree(avltree);
                        break;
                }
            }
        }else{
            bool sucess = DeleteEleFromAVLTreeRecu(&(*avltree)->rchild,deletekey,degrow,realdel);
            if(!sucess)
                return false;
            else if(*degrow){
                *degrow = true;
                switch ((*avltree)->balancefactor)
                {
                    case -1:
                        (*avltree)->balancefactor = 0;
                        break;
                    case 0:
                        (*avltree)->balancefactor = 1;
                        *degrow = true;
                        break;
                    case 1:
                        *degrow = true;
                        (*avltree)->balancefactor = 2;
                        ReBalanceAVLLeftTree(avltree);
                        break;
                }
            }
        }
    }
    return true;
}

bool DeleteEleFromAVLTree(BstNode **avltree,int deletekey ){
    int realdevalue = 1;
    int *realdel = &realdevalue;
    int delkey = deletekey;
    bool degrow = false;
    return DeleteEleFromAVLTreeRecu(avltree , &delkey,&degrow,realdel);
}

bool InsertEleIntoAVLTree(AVLNode **avltree,int insertkey,bool* grow){
    if(*avltree == NULL){
        *avltree = (AVLNode *)malloc(sizeof(AVLNode));
        (*avltree)->data = insertkey;
        (*avltree)->lchild = (*avltree)->rchild = NULL;
        (*avltree)->balancefactor = 0;
        *grow = true;
    }else{
        if((*avltree)->data == insertkey){
            *grow = false;
            return false;
        }else if ((*avltree)->data > insertkey){
            bool success = InsertEleIntoAVLTree(&(*avltree)->lchild,insertkey,grow);
            if(!success)
                return false;
            else if(*grow){
                switch((*avltree)->balancefactor){
                    case 1:
                        (*avltree)->balancefactor = 2;
                        ReBalanceAVLLeftTree(avltree);
                        *grow = false;
                        break;
                    case 0:
                        (*avltree)->balancefactor = 1;*grow = true;
                        break;
                    case -1:
                        (*avltree)->balancefactor = 0;*grow = false;
                        break;
                }
            }
        }else{
            bool success = InsertEleIntoAVLTree(&(*avltree)->rchild,insertkey,grow);
            if(!success)
                return false;
            else if(*grow){
                switch((*avltree)->balancefactor){
                    case -1:
                        (*avltree)->balancefactor = -2;
                        ReBalanceAVLRightTree(avltree);
                        *grow = false;
                        break;
                    case 0:
                        (*avltree)->balancefactor = -1;*grow = true;
                        break;
                    case 1:
                        (*avltree)->balancefactor = 0;*grow = false;
                        break;
                }
            }
        }
    }
    return true;
}

int main(void){
    AVLNode * tree=NULL;
    int arr[] = {22,11,10,5,2,24,25,23,35,10};
    int index = 0;
    for(index = 0 ; index < 9 ; ++index){
        bool flag;
        printf("Input data : %d\n",arr[index]);
        if(InsertEleIntoAVLTree(&tree, arr[index],&flag)){
            printf("#########################\n");
            printf("PreOrderTraverse start!\n");
            PreOrderTraverse(tree);
            printf("PreOrderTraverse stop!\n");
            printf("#########################\n");
            printf("InOrderTraverse start!\n");
            InOrderTraverse(tree);
            printf("InOrderTraverse stop!\n");
            printf("#########################\n");
        }
    }

    int arr2[] = {23,35,2,5,22,10,22,24,25,11,5,2};
    for(index = 0 ; index < 10 ; ++index){
        bool flag;
        printf("Delete data : %d\n",arr2[index]);
        flag = DeleteEleFromAVLTree(&tree, arr2[index]);
        if (flag){
            printf("#########################\n");
            printf("PreOrderTraverse start!\n");
            PreOrderTraverse(tree);
            printf("PreOrderTraverse stop!\n");
            printf("#########################\n");
            printf("InOrderTraverse start!\n");
            InOrderTraverse(tree);
            printf("InOrderTraverse stop!\n");
            printf("#########################\n");
        }
        else{
            printf("Can't find the key!\n");
        }
    }

    return 0;
}
View Code

 

posted @ 2021-12-04 22:01  星座北斗  阅读(124)  评论(0)    收藏  举报