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):
cppAVLNode<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):
cppAVLNode<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树的优势在于其平衡性,但旋转开销可能略高于红黑树,根据需求选择合适结构。