AVL树:平衡二叉搜索树原理与C++实战

AVL树:平衡搜索树的核心概念与C++实现

AVL树是一种自平衡的二叉搜索树(BST),由Adelson-Velskii和Landis在1962年提出。它通过动态调整树的结构,确保在任何操作后树的高度保持O(\\log n),从而优化搜索、插入和删除的时间复杂度。AVL树的核心在于平衡因子旋转操作,本解析将从核心概念逐步过渡到C++实现。


一、核心概念

1. 平衡因子

在AVL树中,每个节点都有一个平衡因子(Balance Factor),定义为该节点左子树高度减去右子树高度的差值: $$ \text{平衡因子} = h_{\text{左}} - h_{\text{右}} $$ 其中h_{\\text{左}}h_{\\text{右}}分别表示左子树和右子树的高度。平衡因子必须在集合{-1, 0, 1}中,否则树不平衡。例如,如果平衡因子为2,表示左子树比右子树高两层,需要调整。

2. 旋转操作

当插入或删除导致平衡因子超出范围时,AVL树通过四种旋转操作恢复平衡:

  • 左旋(Left Rotation):用于右子树过高的情况。
  • 右旋(Right Rotation):用于左子树过高的情况。
  • 左右旋(Left-Right Rotation):先左旋后右旋,处理左子树的右子树过高。
  • 右左旋(Right-Left Rotation):先右旋后左旋,处理右子树的左子树过高。

这些操作确保树的高度最小化,维持O(\\log n)的搜索效率。

3. 高度维护

节点高度定义为: $$ h = \max(h_{\text{左}}, h_{\text{右}}) + 1 $$ 其中叶子节点的高度为0(或1,取决于定义)。在实现中,高度需在每次操作后更新。


二、C++实现细节

在C++中实现AVL树,需定义节点结构、高度计算函数、旋转函数和插入函数。以下逐步解析关键部分。

1. 节点结构

定义一个模板类节点,包含数据、左右子节点指针和高度。

cpp 复制代码
template <typename T>
class AVLNode {
public:
    T key;
    AVLNode* left;
    AVLNode* right;
    int height; // 节点高度

    AVLNode(T k) : key(k), left(nullptr), right(nullptr), height(1) {}
};
2. 高度和平衡因子计算

实现辅助函数获取高度和平衡因子。

cpp 复制代码
int getHeight(AVLNode<T>* node) {
    if (node == nullptr) return 0;
    return node->height;
}

int getBalanceFactor(AVLNode<T>* node) {
    if (node == nullptr) return 0;
    return getHeight(node->left) - getHeight(node->right);
}
3. 旋转操作实现

以右旋和左右旋为例:

  • 右旋(Right Rotation)

    cpp 复制代码
    AVLNode<T>* rightRotate(AVLNode<T>* y) {
        AVLNode<T>* x = y->left;
        AVLNode<T>* T2 = x->right;
    
        // 执行旋转
        x->right = y;
        y->left = T2;
    
        // 更新高度
        y->height = std::max(getHeight(y->left), getHeight(y->right)) + 1;
        x->height = std::max(getHeight(x->left), getHeight(x->right)) + 1;
    
        return x; // 新根节点
    }
  • 左右旋(Left-Right Rotation)

    cpp 复制代码
    AVLNode<T>* leftRightRotate(AVLNode<T>* z) {
        z->left = leftRotate(z->left); // 先左旋左子节点
        return rightRotate(z);         // 再右旋当前节点
    }
4. 插入函数

插入新节点后,递归更新高度并检查平衡因子,必要时旋转。

cpp 复制代码
AVLNode<T>* insert(AVLNode<T>* node, T key) {
    // 标准BST插入
    if (node == nullptr) return new AVLNode<T>(key);
    if (key < node->key) node->left = insert(node->left, key);
    else if (key > node->key) node->right = insert(node->right, key);
    else return node; // 重复键不插入

    // 更新高度
    node->height = 1 + std::max(getHeight(node->left), getHeight(node->right));

    // 获取平衡因子
    int balance = getBalanceFactor(node);

    // 根据平衡因子旋转
    // 左子树高(平衡因子 > 1)
    if (balance > 1 && key < node->left->key) return rightRotate(node); // 右旋
    if (balance > 1 && key > node->left->key) return leftRightRotate(node); // 左右旋

    // 右子树高(平衡因子 < -1)
    if (balance < -1 && key > node->right->key) return leftRotate(node); // 左旋
    if (balance < -1 && key < node->right->key) return rightLeftRotate(node); // 右左旋

    return node; // 无需旋转
}
5. 完整示例:插入过程

假设初始树为空,插入序列\[10, 20, 30\]

  • 插入10:树平衡。
  • 插入20:平衡因子为-1,无问题。
  • 插入30:导致根节点平衡因子为-2,执行左旋恢复平衡。

三、总结

AVL树通过平衡因子和旋转操作维持O(\\log n)的高度,适用于频繁插入和删除的场景。C++实现需注意高度更新和旋转逻辑,确保代码高效。完整实现还包括删除操作和遍历功能,但核心已在上述解析中覆盖。AVL树的优势在于其平衡性,但旋转开销可能略高于红黑树,根据需求选择合适结构。

相关推荐
浩瀚地学2 小时前
【Java】JDK8的一些新特性
java·开发语言·经验分享·笔记·学习
XXOOXRT3 小时前
基于SpringBoot的加法计算器
java·spring boot·后端·html5
阿崽meitoufa3 小时前
JVM虚拟机:垃圾收集器和判断对象是否存活的算法
java·jvm·算法
我是苏苏3 小时前
C#高级:使用ConcurrentQueue做一个简易进程内通信的消息队列
java·windows·c#
heartbeat..5 小时前
数据库基础知识体系:概念、约束、范式与国产产品
java·数据库·学习笔记·国产数据库
PXM的算法星球5 小时前
【操作系统】哲学家就餐问题实现详解
java
2301_815357705 小时前
Java项目架构从单体架构到微服务架构的发展演变
java·微服务·架构
Ethan-D5 小时前
#每日一题19 回溯 + 全排列思想
java·开发语言·python·算法·leetcode