AVL树

简介

  AVL树是最早发明的自平衡二叉查找树。AVL树要求任意节点的两个子树的高度差不大于一,所以是一种高度平衡的二叉查找树。AVL树的高度平衡性使得其查找性能十分优越,但也正是由于其高度平衡性使得每插入或删除一个节点需要一次或多次树旋转来平衡这棵树。因为在大多数环境中AVL树查找快速的优点无法掩盖它插入或删除时操作复杂的缺点,所以UNIX和C++STL中的平衡树大部分使用的是红黑树而不是AVL树。
  AVL树是一棵二叉搜索树,在阅读此篇博客前需要读者具备二叉搜索树的相关知识以及二叉搜索树插入和删除节点的算法基础。

树的高度

  所谓树的高度在AVL树中的定义是:空指针的高度为零,叶子节点的高度为一,非叶子节点的高度为高度较高的子节点的高度加一。
  AVL树时刻要保持平衡,所以如果插入或删除一个节点后树发生了失衡,就需要立即对树进行旋转以保持树的平衡。在AVL树中插入或删除一个节点可以让树的高度不变或者高度加一,不可能出现其他情况。由于AVL树的任意节点的两个子树的高度差不大于一,所以插入或删除节点后若发生树的失衡,其两棵子树的高度差只可能为二。

树的旋转

  AVL树失衡时其高度差只能为二,高度差为二失衡总共有以下六种。

失衡情况一
A
B
H
C
F
D
E
失衡情况二
A
B
H
D
C
E
F
失衡情况三
A
B
K
C
D
E
F
H
I
失衡情况四
A
D
B
E
C
F
H
失衡情况五
A
D
B
C
H
E
F
失衡情况六
A
E
B
C
D
F
G
H
I

  在以上六种情况中情况三和情况六不可能在AVL中出现,因为在出现情况三之前已经出现了情况一和情况二,出现情况六之前已经出现了情况四和情况五。所以最终我们只需要解决四种失衡情况,对应这四种情况有以下四种旋转方式来调节平衡。

左左旋转

A
B
H
C
F
D
E
B
C
A
F
H
D
E

左右旋转

A
B
H
D
C
E
F
C
B
A
D
E
F
H

右右旋转

A
D
B
E
C
F
H
B
A
C
D
E
F
H

右左旋转

A
D
B
C
H
E
F
C
A
B
D
E
F
H

实现代码

C语言

#include <stdio.h>
#include <stdlib.h>
typedef int ELEMENT;
typedef struct __tree_node{
    ELEMENT data;///节点元素
    size_t high;///当前节点的高度
    struct __tree_node *left, *right;///左右子节点指针
}TREE_NODE;
/** \brief 元素比较函数
 *
 * \param x ELEMENT
 * \param y ELEMENT
 * \return int
 *
 */
int element_compare(ELEMENT x,ELEMENT y){
    return x-y;
}
/** \brief 获取节点高度
 *
 * \param root TREE_NODE* 节点或空节点
 * \return int 节点高度,空节点为零
 *
 */
size_t __avl_get_high(TREE_NODE *root){
    return root ? root->high : 0;
}
/** \brief 计算节点高度
 *
 * \param root TREE_NODE* 非空节点
 * \return int 节点高度
 *
 */
size_t __avl_calculate_high(TREE_NODE *root){
    return ((__avl_get_high(root->left)>__avl_get_high(root->right))?__avl_get_high(root->left):__avl_get_high(root->right))+1;
}
/** \brief 左左旋转
 *
 * \param root TREE_NODE* 旋转前根节点
 * \return TREE_NODE* 旋转后根节点
 *
 */
TREE_NODE *__avl_rotate_left_left(TREE_NODE *root){
    TREE_NODE *temp = root->left;
    root->left = temp->right;
    temp->right = root;
    root->high = __avl_calculate_high(root);
    temp->high = __avl_calculate_high(temp);
    return temp;
}
/** \brief 右右旋转
 *
 * \param root TREE_NODE* 旋转前根节点
 * \return TREE_NODE* 旋转后根节点
 *
 */
TREE_NODE *__avl_rotate_right_right(TREE_NODE *root){
    TREE_NODE *temp = root->right;
    root->right = temp->left;
    temp->left = root;
    root->high = __avl_calculate_high(root);
    temp->high = __avl_calculate_high(temp);
    return temp;
}
/** \brief 左右旋转
 *
 * \param root TREE_NODE* 旋转前根节点
 * \return TREE_NODE* 旋转后根节点
 *
 */
TREE_NODE *__avl_rotate_left_right(TREE_NODE *root){
    TREE_NODE *temp = root->left->right;
    root->left->right = temp->left;
    temp->left = root->left;
    root->left = temp->right;
    temp->right = root;
    root->high = __avl_calculate_high(root);
    temp->high = __avl_calculate_high(temp);
    temp->left->high = __avl_calculate_high(temp->left);
    return temp;
}
/** \brief 右左旋转
 *
 * \param root TREE_NODE* 旋转前根节点
 * \return TREE_NODE* 旋转后根节点
 *
 */
TREE_NODE *__avl_rotate_right_left(TREE_NODE *root){
    TREE_NODE *temp = root->right->left;
    root->right->left = temp->right;
    temp->right = root->right;
    root->right = temp->left;
    temp->left = root;
    root->high = __avl_calculate_high(root);
    temp->high = __avl_calculate_high(temp);
    temp->right->high = __avl_calculate_high(temp->right);
    return temp;
}
/** \brief 左旋转(非右子树旋转)
 *
 * \param root TREE_NODE* 旋转前根节点
 * \return TREE_NODE* 旋转后的根节点
 *
 */
TREE_NODE *__avl_rotate_left(TREE_NODE *root){
    if(__avl_get_high(root->left->left) < __avl_get_high(root->left->right)){  ///左子树的右子树高度高则左右旋转
        root = __avl_rotate_left_right(root);
    }else{
        root = __avl_rotate_left_left(root);
    }
    return root;
}
/** \brief 右旋转(非左子树旋转)
 *
 * \param root TREE_NODE* 旋转前根节点
 * \return TREE_NODE* 旋转后根节点
 *
 */
TREE_NODE *__avl_rotate_right(TREE_NODE *root){
    if(__avl_get_high(root->right->right) < __avl_get_high(root->right->left)){  ///右子树的左子树高度高则右左旋转
        root = __avl_rotate_right_left(root);
    }else{
        root = __avl_rotate_right_right(root);
    }
    return root;
}
/** \brief 左调整(非右子树调整)
 *
 * \param root TREE_NODE* 调整前的根节点
 * \return TREE_NODE* 调整后的根节点
 *
 */
TREE_NODE *__avl_adjust_left(TREE_NODE *root){
    if(__avl_get_high(root->left)-__avl_get_high(root->right) == 2){///左子树是否过高
        root = __avl_rotate_left(root);
    }else{///旋转函数中含有高度计算,如果树并未失衡则需要单独计算高度
        root->high = __avl_calculate_high(root);
    }
    return root;
}
/** \brief 右调整(非左子树调整)
 *
 * \param root TREE_NODE* 调整前的根节点
 * \return TREE_NODE* 调整后的根节点
 *
 */
TREE_NODE *__avl_adjust_right(TREE_NODE *root){
    if(__avl_get_high(root->right)-__avl_get_high(root->left) == 2){///右子树是否过高
        root = __avl_rotate_right(root);
    }else{///旋转函数中含有高度计算,如果树并未失衡则需要单独计算高度
        root->high = __avl_calculate_high(root);
    }
    return root;
}
/** \brief 创建节点
 *
 * \param data ELEMENT 节点内的数据
 * \return TREE_NODE* 节点指针
 *
 */
TREE_NODE *create_node(ELEMENT data){
    TREE_NODE *temp = (TREE_NODE *)malloc(sizeof(TREE_NODE));
    if(temp){
        temp->data = data;
        temp->high = 1;
        temp->left = NULL;
        temp->right = NULL;
    }
    return temp;
}
/** \brief 向树中插入节点
 *
 * \param root TREE_NODE* 旋转前根节点
 * \param data ELEMENT 需要插入的节点
 * \return TREE_NODE* 插入后的根节点
 *
 */
TREE_NODE *avl_insert(TREE_NODE *root,ELEMENT data){
    int diff;
    if(root){
        diff = element_compare(data,root->data);
        if(diff < 0){///向左子树中插入节点
            root->left = avl_insert(root->left,data);///递归插入节点
            root = __avl_adjust_left(root);
        }else if(0 < diff){///向右子树中插入节点
            root->right = avl_insert(root->right,data);///递归插入节点
            root = __avl_adjust_right(root);
        }
    }else{///根节点为空则将插入节点作为根节点
        root = create_node(data);
    }
    return root;
}
/** \brief 移出树中最左侧节点
 *
 * \param root TREE_NODE** 指向根节点指针的指针
 * \return TREE_NODE* 指向移出节点的指针
 *
 */
TREE_NODE *avl_remove_leftmost(TREE_NODE **root){
    TREE_NODE *temp;
    if((*root)->left){
        temp = avl_remove_leftmost(&(*root)->left);///递归找到最左侧节点
        *root = __avl_adjust_right(*root);
    }else{
        temp = *root;
        *root = temp->right;
    }
    return temp;
}
/** \brief 移出树中最右侧节点
 *
 * \param root TREE_NODE** 指向根节点指针的指针
 * \return TREE_NODE* 指向移出节点的指针
 *
 */
TREE_NODE *avl_remove_rightmost(TREE_NODE **root){
    TREE_NODE *temp;
    if((*root)->right){
        temp = avl_remove_rightmost(&(*root)->right);///递归找到最右侧节点
        *root = __avl_adjust_left(*root);
    }else{
        temp = *root;
        *root = temp->left;
    }
    return temp;
}
/** \brief 移出树的根节点
 *
 * \param root TREE_NODE** 指向根节点指针的指针
 * \return TREE_NODE* 指向移出节点的指针
 *
 */
TREE_NODE *remove_root(TREE_NODE **root){
    TREE_NODE *temp = *root;
    if(temp->left||temp->right){///该节点是否存在子节点
        if(__avl_get_high(temp->right) < __avl_get_high(temp->left)){///如果左子树高则从左子树中选取新的根节点
            *root = avl_remove_rightmost(&(temp->left));///移出左子树中最右侧节点作为新的根节点
            (*root)->left = temp->left;
            (*root)->right = temp->right;
            *root = __avl_adjust_right(*root);
        }else{
            *root = avl_remove_leftmost(&(temp->right));///移出右子树中最左侧节点作为新的根节点
            (*root)->left = temp->left;
            (*root)->right = temp->right;
            *root = __avl_adjust_left(*root);
        }
    }else{
        *root = NULL;
    }
    return temp;
}
/** \brief 移出节点
 *
 * \param root TREE_NODE** 指向根节点指针的指针
 * \param data ELEMENT 需要移出的数据
 * \return TREE_NODE* 指向移出的节点,如果没有返回为空
 *
 */
TREE_NODE *avl_remove(TREE_NODE **root,ELEMENT data){
    TREE_NODE *temp;
    int diff;
    if(*root){
        diff = element_compare(data,(*root)->data);
        if(diff<0){
            temp = avl_remove(&(*root)->left,data);///递归
            *root = __avl_adjust_right(*root);
        }else if(diff>0){
            temp = avl_remove(&(*root)->right,data);///递归
            *root = __avl_adjust_left(*root);
        }else{///找到目标节点
            temp = remove_root(root);
        }
        return temp;
    }
    return NULL;///空节点返回空
}
/** \brief 查找节点
 *
 * \param root TREE_NODE* 根节点指针
 * \param data ELEMENT 目标元素
 * \return TREE_NODE* 指向目标节点的指针
 *
 */
TREE_NODE *avl_find(TREE_NODE *root,ELEMENT data){
    int diff;
    if(root){
        diff = element_compare(data,root->data);
        if(diff<0){///目标值小于该节点向左子树递归查找
            root = avl_find(root->left,data);
        }else if(diff>0){///目标值大于该节点向右子树递归查找
            root = avl_find(root->right,data);
        }///目标值等于该节点则不会陷入递归直接返回该节点
    }
    return root;
}

void print(TREE_NODE *node){
    printf("%d %d\n",node->data,node->high);
}
void preorder_base(TREE_NODE *root){
    if(root){
        print(root);
        preorder_base(root->left);
        preorder_base(root->right);
    }
}
void preorder(TREE_NODE *root){
    printf("preoder:\n");
    preorder_base(root);
}
void inorder_base(TREE_NODE *root){
    if(root){
        inorder_base(root->left);
        print(root);
        inorder_base(root->right);
    }
}
void inorder(TREE_NODE *root){
    printf("inorder:\n");
    inorder_base(root);
}

int main(){
    int test[] = {
        89,70,52,48,78,52,47,55,99
    };
    TREE_NODE *root = NULL;
    for(size_t i=0;i<sizeof(test)/sizeof(ELEMENT);i++){
        root = avl_insert(root,test[i]);
    }
    inorder(root);
    preorder(root);

    if(avl_find(root,test[2])){
        printf("find success\n");
    }else{
        printf("find fail\n");
    }
    if(avl_remove(&root,test[2])){
        printf("remove success\n");
    }else{
        printf("remove fail\n");
    }
    if(avl_find(root,test[2])){
        printf("find success\n");
    }else{
        printf("find fail\n");
    }

    printf("remove min: %d\n",avl_remove_leftmost(&root)->data);
    inorder(root);
    preorder(root);

    printf("remove min: %d\n",avl_remove_leftmost(&root)->data);
    inorder(root);
    preorder(root);

    printf("remove min: %d\n",avl_remove_leftmost(&root)->data);
    inorder(root);
    preorder(root);

    printf("remove max: %d\n",avl_remove_rightmost(&root)->data);
    inorder(root);
    preorder(root);

    printf("remove max: %d\n",avl_remove_rightmost(&root)->data);
    inorder(root);
    preorder(root);

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值