AVL树

本文深入探讨AVL树的概念,一种自平衡二叉查找树,重点讲解树的高度、旋转及其实现细节,包括左左旋转、左右旋转、右右旋转和右左旋转等关键操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

  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;
}
07-07
AVL是一种自平衡的二叉查找,它确保了高度始终保持在对数级别,从而保证了查找、插入和删除操作的时间复杂度为O(log n)。这种数据结构以它的发明者G.M. Adelson-Velsky和E.M. Landis的名字命名[^1]。 ### AVL的定义 - **平衡因子**:每个节点都有一个平衡因子,它是该节点左子高度减去右子高度。对于AVL来说,所有节点的平衡因子只能是-1, 0或1。 - **空**:如果T是空,则它自然是一个AVL。 - **非空**:若T不是空,则其左右子TL和TR都必须是AVL,并且对于任意节点,|HL - HR| ≤ 1(其中HL和HR分别表示左子和右子高度)。 ### AVL的操作 当进行插入或删除操作时,可能会破坏AVL的平衡性,这时需要通过旋转来重新恢复平衡: - **单旋转**: - 左单旋(Single Rotate with Left)用于处理左孩子的左子过高。 - 右单旋(Single Rotate with Right)用于处理右孩子的右子过高。 - **双旋转**: - 左右双旋(Double Rotate with Left)用于处理左孩子的右子过高的情况。 - 右左双旋(Double Rotate with Right)用于处理右孩子的左子过高的情况。 这些旋转操作可以保持AVL的性质不变,并且能够快速地调整的结构以维持平衡。 ### AVL的实现 下面是一个简单的C语言代码示例,展示了如何定义AVL的节点以及实现基本的插入操作。这个例子中包含了必要的函数声明和一些核心逻辑。 ```c #include <stdio.h> #include <stdlib.h> // 定义AVL节点结构体 typedef struct AvlNode { int element; struct AvlNode *left; struct AvlNode *right; int height; // 节点的高度 } *AvlTree, *Position; // 获取两个整数中的较大者 int max(int a, int b) { return (a > b) ? a : b; } // 计算给定节点的高度 static int Height(AvlTree T) { if (T == NULL) return -1; // 空节点高度为-1 else return T->height; } // 单旋转 - 左左情况 AvlTree singlerotatewithLeft(AvlTree K2) { Position K1 = K2->left; K2->left = K1->right; K1->right = K2; // 更新节点高度 K2->height = max(Height(K2->left), Height(K2->right)) + 1; K1->height = max(Height(K1->left), K2->height) + 1; return K1; // 新根 } // 单旋转 - 右右情况 AvlTree singlerotatewithRight(AvlTree K2) { Position K1 = K2->right; K2->right = K1->left; K1->left = K2; // 更新节点高度 K2->height = max(Height(K2->left), Height(K2->right)) + 1; K1->height = max(Height(K1->right), K2->height) + 1; return K1; // 新根 } // 双旋转 - 左右情况 AvlTree doublerotatewithLeft(AvlTree K3) { K3->left = singlerotatewithRight(K3->left); return singlerotatewithLeft(K3); } // 双旋转 - 右左情况 AvlTree doublerotatewithRight(AvlTree K3) { K3->right = singlerotatewithLeft(K3->right); return singlerotatewithRight(K3); } // 插入新元素到AVLAvlTree Insert(int x, AvlTree T) { if (T == NULL) { // 如果为空,创建新节点 T = (AvlTree)malloc(sizeof(struct AvlNode)); if (T == NULL) printf("Out of space!!!"); else { T->element = x; T->left = T->right = NULL; T->height = 0; // 新叶节点高度为0 } } else if (x < T->element) { // 向左子插入 T->left = Insert(x, T->left); // 检查并修复平衡 if (Height(T->left) - Height(T->right) == 2) { if (x < T->left->element) T = singlerotatewithLeft(T); // 左左旋转 else T = doublerotatewithLeft(T); // 左右旋转 } } else if (x > T->element) { // 向右子插入 T->right = Insert(x, T->right); // 检查并修复平衡 if (Height(T->right) - Height(T->left) == 2) { if (x > T->right->element) T = singlerotatewithRight(T); // 右右旋转 else T = doublerotatewithRight(T); // 右左旋转 } } // 更新高度 T->height = max(Height(T->left), Height(T->right)) + 1; return T; } ``` 上述代码提供了AVL的基本框架,包括节点定义、插入操作及必要的旋转方法。实际应用中可能还需要添加更多的功能,如删除节点、查找特定值等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值