数据结构-平衡二叉树

436 阅读6分钟

开场白

紧接上篇二叉排序树最后遗留的问题,这篇聊聊平衡二叉树(AVL树)。

平衡二叉树

平衡二叉树,是一种二叉排序树。其中每一个结点的左子树和右子树的高度差最大等于1。

相关概念

⾼度平衡: 意思是说,要么它是⼀颗空树,要么它的左⼦树和右⼦树都是平衡⼆叉树.且左⼦树和右⼦树的深度之差的绝对值不超过1; 我们将⼆叉树上结点的左⼦树深度减去右⼦树深度的值称为平衡因⼦BF(Balance Factr)

最小不平衡子树: 距离插⼊点最近的,且平衡因⼦的绝对值⼤于1的结点为根的⼦树。

构建思想

就是在构建⼆叉排序树的过程中,每当插⼊⼀个结点时,先检查是否因插⼊⽽破坏了树的平衡性。若是,则找到最⼩不平衡⼦树.在保持⼆叉排序树特性的前提下,调整最⼩不平衡⼦树中各结点之间的链接关系.进⾏相应的旋转,使之成为新的平衡⼦树。

模拟构建过程

数组a[10] = {3,2,1,4,5,6,7,10,9,8},模拟构建过程,结点结构体中增加bf变量,表示平衡因子

typedef struct BiTNode{
    int data;
    int bf;//平衡因子
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

开始模拟:

  1. 插入结点3:因为只有一个根结点,所以树是平衡的
  2. 插入结点2:结点3->bf=1(左子树深度为1,右子树深度为0,左-右=1),结点2->bf=0
  3. 插入结点1:3->bf=2,2->bf=1,1->bf=0。
    当前树失衡,最小不平衡子树的根结点是3。需要右旋。右旋后树平衡,各个结点的bf都为0
    注意:3->bf=2,2->bf=1,同为正数时进行右旋;如果同为负数进行左旋;如果一正一负,进行双旋,正右旋,负左旋。优先处理子树。如果子树为正,根为负:子树右旋后,总体左旋。反之同理。
  4. 插入结点4:所有结点bf绝对值没有超过1的,所以树是平衡的
  5. 插入结点5:各结点的bf变化如图:
    最小不平衡子树根结点是3,bf=-2(计算方法:左子树深度为0,右子树深度为2,0-2=-2)。右孩子结点4的bf=-1。同时为负数,所以需要左旋。
  6. 插入结点6:各结点的bf变化如图:
    此时最小不平衡子树根结点是2,bf=-2,而且子树中的bf没有正数,所以需要左旋:
    左旋后结点4为根结点,结点2变成左子树,但结点4原状态是有左子树的,结点为3。所以根据二叉排序树逻辑,结点3变成结点2的右子树。
  7. 插入结点7:
    最小不平衡子树根结点为5,需要左旋处理
  8. 插入结点10:树处于平衡状态,无需调整
  9. 插入结点9:
    最小不平衡树根结点是7,bf=-2。子树中结点10,bf=1。一正一负,需要双旋处理。子树为正,子树先进行右旋,结点7的树再左旋。如图:
  10. 插入结点8:
    最小不平衡子树根结点6,bf=-2,需要左旋。子树9的bf=1,需要右旋,先处理子树右旋
    再处理根结点6的树进行左旋

代码

1.旋转

//右旋
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;
}

2.bf取值

图示中bf出现了2的情况,其实代码中只需要标示正负关系即可。

#define LH +1 /*左高*/
#define EH 0 /*等高*/
#define RH -1 /*右高*/

3.左右平衡

//左平衡函数: 树bf=LH时,调用
void LeftBalance(BiTree *T) {
    BiTree L, Lr;
    
    //获取左子树
    L = (*T)->lchild;
    
    //根据L的bf分别处理,逻辑:先修改状态,再调整树
    switch (L->bf) {
        case LH://根结点是LH状态,左子树是LH状态
            //修改根结点和左子树状态为EH
            (*T)->bf=L->bf=EH;
            //右旋树
            R_Rotate(T);
            break;
            
        case RH://根结点是LH状态,做子树是RH状态,进行双旋,先左旋,后右旋
            //获取左子树的右子树,该结点会成为根结点
            Lr=L->rchild;
            
            //根据左子树的右子树bf分别处理,逻辑:先修改状态,再调整数
            //修改状态
            switch (Lr->bf) {
                case LH:
                    (*T)->bf=RH;
                    L->bf=EH;
                    break;
                    
                case EH:
                    (*T)->bf=L->bf=EH;
                    break;
                    
                case RH:
                    (*T)->bf=EH;
                    L->bf=LH;
                    break;
            }
            Lr->bf=EH;
            //调整树
            L_Rotate(&(*T)->lchild);
            R_Rotate(T);
            
            break;
            
        default:
            break;
    }
}

//右平衡函数: 树bf=RH时,调用
void RightBalance(BiTree *T) {
    BiTree R, Rl;
    //获取右子树
    R = (*T)->rchild;
    
    switch (R->bf) {
        case RH://根结点bf=RH,右子树bf=RH时
            (*T)->bf=R->bf=EH;
            //左旋转
            L_Rotate(T);
            break;
            
        case LH://根结点bf=RH,右子树bf=LH时,进行双循,先右旋,后左旋
            //获取右子树的左子树
            Rl=R->lchild;
            //修改状态
            switch (Rl->bf) {
                case RH:
                    (*T)->bf=LH;
                    R->bf=EH;
                    break;
                    
                case EH:
                    (*T)->bf=R->bf=EH;
                    break;
                    
                case LH:
                    (*T)->bf=EH;
                    R->bf=RH;
                    break;
            }
            Rl->bf=EH;
            //选装
            R_Rotate(&(*T)->rchild);
            L_Rotate(T);
            break;
            
        default:
            break;
    }
}

4.插入

//插入函数,递归函数,taller参数是插入后,树是否长高
Status InsertAVL(BiTree *T, int e, Status *taller) {
    if (!(*T)) {//树为空时处理,递归出口
        *T= (BiTree)malloc(sizeof(BiTNode));
        (*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:
                        //左旋函数
                        LeftBalance(T);
                        //树不长高
                        *taller = FALSE;
                        break;
                        
                    case EH:
                        (*T)->bf=LH;
                        *taller = TRUE;
                        break;
                        
                    case RH:
                        (*T)->bf=EH;
                        *taller=FALSE;
                        
                    default:
                        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;
                        
                    default:
                        break;
                }
            }
        }
    }
    return TRUE;
}

5.查找

//二叉排序树的查找
Status SearchBST(BiTree T, int key, BiTree f, BiTree *p) {
    if (!T) {
        *p = f;
        return FALSE;
    } else if (key == T->data) {
        *p = T;
        return TRUE;
    } else if (key < T->data) {
        return SearchBST(T->lchild, key, T, p);
    } else {
        return SearchBST(T->rchild, key, T, p);
    }
}

6.运行

int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, 平衡二叉树!\n");
    int i;
    int a[10]={3,2,1,4,5,6,7,10,9,8};
    //调整数组的顺序,最终生成的平衡二叉树高度是一样的.
    //int a[10]={8,9,1,4,5,6,7,10,2,3};
    //int a[10]={9,4,1,2,7,6,5,10,3,8};
    
    BiTree T=NULL;
    Status taller;
    int sum = 0;
    for(i=0;i<10;i++)
    {
        InsertAVL(&T,a[i],&taller);
        sum += taller;
        printf("插入%d,是否增加树的高度(%d)[YES->1 / NO->0]\n",a[i],taller);
    }
    
    printf("将数组a插入到平衡二叉树后,最终形成高度为%d的平衡二叉树\n",sum);
    
    BiTree p;
    int statusValue = SearchBST(T, 10, NULL, &p);
    printf("查找%d是否成功:%d (1->YES/0->NO)\n",p->data,statusValue);
    return 0;
}


补充

上篇二叉排序树,最后的极端例子{35,37,47,51,58,62,73,88,93,99}

根据平衡二叉树的构建思想最后会生成如图的树:
树的深度只有3,搜索起来效率更高。