C++进阶:(六)深入浅出分析AVL树:原理与实现
目录
前言
一、AVL 树的核心概念
1.1 定义与性质
1.2 平衡因子(Balance Factor)
1.3 为什么不要求高度差为 0?
1.4 AVL 树的高度与性能
二、AVL 树的结构设计(C++)
2.1 节点结构(AVLTreeNode)
2.2 树结构(AVLTree)
三、AVL 树的插入实现
3.1 插入流程概述
3.2 步骤 1:按二叉搜索树规则插入
3.3 步骤 2:回溯更新平衡因子
3.3.1 平衡因子更新原则
3.3.2 平衡因子更新停止条件
3.3.3 代码实现:更新平衡因子
四、AVL 树的旋转操作(平衡调整核心)
4.1 右单旋(RotateR):处理 LL 型失衡
4.1.1 失衡场景
4.1.2 旋转原理
4.1.3 代码实现
4.2 左单旋(RotateL):处理 RR 型失衡
4.2.1 失衡场景
4.2.2 旋转原理
4.2.3 代码实现
4.3 左右双旋(RotateLR):处理 LR 型失衡
4.3.1 失衡场景
4.3.2 旋转原理
4.3.3 平衡因子重置逻辑
4.3.4 代码实现
4.4 右左双旋(RotateRL):处理 RL 型失衡
4.4.1 失衡场景
4.4.2 旋转原理
4.4.3 平衡因子重置逻辑
4.4.4 代码实现
五、AVL 树的查找实现
5.1 查找代码实现
5.2 查找示例
六、AVL 树的平衡检测
6.1 辅助函数:计算节点高度
6.2 平衡检测函数
6.3 测试代码
6.4 测试结果分析
七、AVL 树的删除(扩展内容)
总结
前言
在数据结构的世界里,二叉搜索树(BST)是一种高效的动态查找结构,其插入、删除和查找操作的时间复杂度均为 O (h)(h 为树的高度)。然而,普通二叉搜索树存在一个致命缺陷:当插入序列有序或接近有序时,树会退化为单链表,此时操作时间复杂度骤降至 O (n),完全失去了高效性。
为解决这一问题,自平衡二叉搜索树应运而生。AVL 树作为最早发明的自平衡二叉搜索树,由前苏联科学家 G. M. Adelson-Velsky 和 E. M. Landis 于 1962 年在论文《An algorithm for the organization of information》中提出。它通过严格控制左右子树的高度差,确保树始终保持平衡状态,从而将操作时间复杂度稳定在 O (log n) 级别。
本文将从 AVL 树的基本概念出发,详细讲解其结构设计、插入逻辑、平衡调整、查找实现及平衡检测方法,并提供完整的代码实现,帮助大家深入理解并掌握这一经典数据结构。下面就让我们正式开始吧!
一、AVL 树的核心概念
1.1 定义与性质
AVL 树是一种特殊的二叉搜索树,满足以下两个核心条件:
- 二叉搜索树性质:左子树中所有节点的键值小于根节点键值,右子树中所有节点的键值大于根节点键值,且左右子树也均为二叉搜索树;
- 高度平衡性质:任意节点的左右子树高度差的绝对值不超过 1,且左右子树也均为 AVL 树。
1.2 平衡因子(Balance Factor)
为了方便判断节点是否平衡,引入平衡因子的概念:
- 定义:节点的平衡因子 = 右子树高度 - 左子树高度;
- AVL 树要求:任意节点的平衡因子只能是 - 1、0 或 1。
平衡因子就像 “风向标”,直观反映了节点左右子树的高度关系,是后续我们进行平衡调整操作的核心判断依据。


1.3 为什么不要求高度差为 0?
有人可能会有这样的疑问:既然要平衡,为什么不要求左右子树高度差为 0?这样不就完全平衡了吗?实际上,这是由树的节点数量决定的 —— 某些情况下,完全平衡是无法实现的。
例如:
- 当树只有 2 个节点时:根节点有一个左孩子或右孩子,左右子树高度差为 1;
- 当树有 4 个节点时:若根节点有左、右子树,左子树有一个孩子,右子树无孩子(或反之),高度差为 1。
因此,AVL 树选择 “高度差绝对值不超过 1” 作为平衡标准,既保证了树的高效性,又避免了无法实现的极端要求。
1.4 AVL 树的高度与性能
AVL 树的高度严格控制在 O (log n) 级别(n 为节点总数),这是因为其平衡性质限制了树的 “生长”:
- 对于 n 个节点的 AVL 树,最小高度为⌊log₂n⌋(接近完全二叉树);
- 最大高度约为 1.44log₂(n+2)-1.328,远小于普通二叉搜索树的最坏情况(O (n))。
因此,AVL 树的插入、删除、查找操作时间复杂度均为 O (log n),相比普通二叉搜索树有本质提升。
二、AVL 树的结构设计(C++)
AVL 树的节点需要存储键值对、左右孩子指针、父节点指针(用于后续平衡因子更新)以及平衡因子。下面通过 C++ 模板来实现一下 AVL 树的节点结构和树结构。
2.1 节点结构(AVLTreeNode)
template<class K, class V>
struct AVLTreeNode {// 存储键值对(适用于字典场景,如key-value映射)pair<K, V> _kv;// 左右孩子指针AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;// 父节点指针(关键:用于插入后回溯更新平衡因子)AVLTreeNode<K, V>* _parent;// 平衡因子(初始为0,新节点无孩子)int _bf;// 构造函数:初始化节点AVLTreeNode(const pair<K, V>& kv): _kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _bf(0) {}
};
说明:
- 父节点指针
_parent是 AVL 树的关键设计之一:插入新节点后,需要从新节点向上回溯,更新所有祖先节点的平衡因子,父节点指针确保了回溯的可行性; - 平衡因子
_bf初始为 0:新节点无左、右孩子,左右子树高度差为 0。
2.2 树结构(AVLTree)
template<class K, class V>
class AVLTree {// 类型别名:简化节点指针的使用typedef AVLTreeNode<K, V> Node;
public:// 构造函数:默认初始化根节点为nullptr(空树)AVLTree() : _root(nullptr) {}// 后续实现:插入、查找、平衡检测等成员函数bool Insert(const pair<K, V>& kv);Node* Find(const K& key);bool IsBalanceTree();// 中序遍历(用于验证二叉搜索树性质)void InOrder() { _InOrder(_root); cout << endl; }private:// 根节点(私有成员,通过公有接口访问)Node* _root;// 辅助函数:递归中序遍历void _InOrder(Node* root) {if (root == nullptr) return;_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}// 辅助函数:计算节点高度int _Height(Node* root);// 辅助函数:递归检测平衡bool _IsBalanceTree(Node* root);// 旋转操作(私有:仅内部平衡调整使用)void RotateR(Node* parent); // 右单旋void RotateL(Node* parent); // 左单旋void RotateLR(Node* parent); // 左右双旋void RotateRL(Node* parent); // 右左双旋
};
说明:
- 采用模板设计:支持任意可比较的键类型
K和值类型V,通用性更强; - 私有辅助函数:
_InOrder(中序遍历)、_Height(计算高度)、_IsBalanceTree(平衡检测)等,封装内部实现细节; - 旋转操作:
RotateR、RotateL等为私有成员,仅在插入后平衡调整时调用,外部无需感知。
三、AVL 树的插入实现
AVL 树的插入是其核心操作,分为 “按二叉搜索树规则插入” 和 “回溯更新平衡因子并调整平衡” 两个阶段。
3.1 插入流程概述
- 按二叉搜索树规则插入:找到新节点的插入位置,创建节点并建立父子关系;
- 回溯更新平衡因子:从新节点的父节点开始,向上更新所有祖先节点的平衡因子,直到根节点或满足停止条件;
- 平衡检测与调整:若更新过程中某节点的平衡因子变为 ±2(失衡),则通过旋转操作调整子树平衡,调整后插入结束(旋转会降低子树高度,不影响上层节点)。
3.2 步骤 1:按二叉搜索树规则插入
首先,按照普通二叉搜索树的插入逻辑找到插入位置,代码如下:
bool AVLTree<K, V>::Insert(const pair<K, V>& kv) {// 情况1:树为空,直接创建根节点if (_root == nullptr) {_root = new Node(kv);return true;}// 情况2:树非空,查找插入位置Node* parent = nullptr;Node* cur = _root;while (cur) {if (cur->_kv.first < kv.first) {// 键值比当前节点大,去右子树查找parent = cur;cur = cur->_right;} else if (cur->_kv.first > kv.first) {// 键值比当前节点小,去左子树查找parent = cur;cur = cur->_left;} else {// 键值已存在,插入失败(AVL树不允许重复键)return false;}}// 找到插入位置,创建新节点并建立父子关系cur = new Node(kv);if (parent->_kv.first < kv.first) {// 新节点为父节点的右孩子parent->_right = cur;} else {// 新节点为父节点的左孩子parent->_left = cur;}// 建立父节点指针(关键:用于后续回溯)cur->_parent = parent;// 后续步骤:更新平衡因子并调整平衡(见3.3节)// ...
}
细节分析:
- 不允许重复键:若插入的键已存在,直接返回
false; - 建立父节点指针:新节点的
_parent指向其父节点,父节点的_left或_right指向新节点,确保回溯路径完整。
3.3 步骤 2:回溯更新平衡因子
插入新节点后,只有新节点的祖先节点的高度可能发生变化(子树高度变化会影响平衡因子)。因此,需要从父节点开始向上更新平衡因子,并判断是否需要停止或调整平衡。
3.3.1 平衡因子更新原则
- 平衡因子定义:
_bf = 右子树高度 - 左子树高度; - 更新逻辑:
- 若新节点是父节点的左孩子:父节点的左子树高度增加 1,因此
parent->_bf--; - 若新节点是父节点的右孩子:父节点的右子树高度增加 1,因此
parent->_bf++;
- 若新节点是父节点的左孩子:父节点的左子树高度增加 1,因此
- 停止条件:根据更新后父节点的平衡因子,决定是否继续向上更新(见下表)。
3.3.2 平衡因子更新停止条件
| 更新后父节点的平衡因子 | 含义(更新前后的变化) | 是否继续向上更新 | 原因 |
|---|---|---|---|
| 0 | 之前为 ±1,插入后变为 0 | 否 | 插入前父节点子树 “一高一低”,插入后 “等高”,子树高度不变,不影响上层节点 |
| ±1 | 之前为 0,插入后变为 ±1 | 是 | 插入前父节点子树 “等高”,插入后 “一高一低”,子树高度增加 1,影响上层节点 |
| ±2 | 之前为 ±1,插入后变为 ±2 | 否(需旋转) | 插入前父节点子树 “一高一低”,插入后 “更高的一侧更突出”,子树失衡,需旋转调整 |



3.3.3 代码实现:更新平衡因子
// 接3.2节代码,继续在Insert函数中实现
// 步骤2:回溯更新平衡因子
while (parent) {// 1. 更新当前父节点的平衡因子if (cur == parent->_left) {// 新节点是父节点的左孩子,父节点bf--parent->_bf--;} else {// 新节点是父节点的右孩子,父节点bf++parent->_bf++;}// 2. 判断是否需要继续更新或调整平衡if (parent->_bf == 0) {// 情况1:bf变为0,子树高度不变,停止更新break;} else if (parent->_bf == 1 || parent->_bf == -1) {// 情况2:bf变为±1,子树高度增加1,继续向上更新cur = parent;parent = parent->_parent;} else if (parent->_bf == 2 || parent->_bf == -2) {// 情况3:bf变为±2,子树失衡,需要旋转调整// 根据失衡类型选择对应的旋转操作if (parent->_bf == 2) {// 右子树过高,需左单旋或右左双旋if (cur->_bf == 1) {// 右子树的右子树过高(RR型),左单旋RotateL(parent);} else {// 右子树的左子树过高(RL型),右左双旋RotateRL(parent);}} else { // parent->_bf == -2// 左子树过高,需右单旋或左右双旋if (cur->_bf == -1) {// 左子树的左子树过高(LL型),右单旋RotateR(parent);} else {// 左子树的右子树过高(LR型),左右双旋RotateLR(parent);}}// 旋转后子树高度恢复,无需继续向上更新,插入结束break;} else {// 异常情况:bf绝对值超过2(代码逻辑错误)assert(false);}
}return true;
代码说明如下:
- 循环条件:
parent != nullptr(直到根节点); - 失衡判断:当
parent->_bf == ±2时,根据cur->_bf(失衡子树的子节点平衡因子)判断失衡类型(LL、RR、LR、RL),选择对应的旋转操作; - 异常处理:若
parent->_bf为其他值(如 3、-3),说明代码逻辑错误,通过assert断言提示。
四、AVL 树的旋转操作(平衡调整核心)
当某节点的平衡因子变为 ±2 时,子树失衡,需要通过旋转操作调整平衡。旋转的核心目标有两个:
- 恢复子树的平衡(任意节点的平衡因子回到 ±1 或 0);
- 降低子树的高度(恢复到插入前的高度,避免影响上层节点)。
根据失衡子树的结构,旋转分为四种类型:右单旋(LL 型)、左单旋(RR 型)、左右双旋(LR 型)、右左双旋(RL 型)。
4.1 右单旋(RotateR):处理 LL 型失衡
4.1.1 失衡场景
LL 型失衡:失衡节点(设为parent)的平衡因子为 - 2(左子树过高),且其左孩子(设为subL)的平衡因子为 - 1(左子树的左子树过高)。
例如:
parent (bf=-2)/subL (bf=-1)/newNode
4.1.2 旋转原理
将subL提升为新的子树根节点,parent变为subL的右孩子,subL的右子树(设为subLR)变为parent的左子树。具体步骤如下:
- 保存
subL的右子树subLR; - 将
subLR作为parent的左子树(若subLR非空,更新其_parent为parent); - 将
parent作为subL的右子树,更新parent的_parent为subL; - 处理
subL与上层节点的关系(若parent是原根节点,则subL成为新根;否则,subL替换parent在其上层父节点中的位置); - 重置
parent和subL的平衡因子为 0(旋转后子树平衡)。
4.1.3 代码实现
void AVLTree<K, V>::RotateR(Node* parent) {// 1. 保存关键节点Node* subL = parent->_left; // parent的左孩子(即将成为新根)Node* subLR = subL->_right; // subL的右子树(即将成为parent的左子树)Node* parentParent = parent->_parent; // parent的父节点(上层节点)// 2. 调整subLR与parent的关系parent->_left = subLR;if (subLR != nullptr) {subLR->_parent = parent;}// 3. 调整parent与subL的关系subL->_right = parent;parent->_parent = subL;// 4. 调整subL与上层节点的关系if (parentParent == nullptr) {// 情况1:parent是原根节点,subL成为新根_root = subL;subL->_parent = nullptr;} else {// 情况2:parent是上层节点的左/右孩子,subL替换其位置if (parentParent->_left == parent) {parentParent->_left = subL;} else {parentParent->_right = subL;}subL->_parent = parentParent;}// 5. 重置平衡因子(旋转后subL和parent均平衡)parent->_bf = 0;subL->_bf = 0;
}
示例图解如下:





4.2 左单旋(RotateL):处理 RR 型失衡
4.2.1 失衡场景
RR 型失衡:失衡节点(parent)的平衡因子为 2(右子树过高),且其右孩子(subR)的平衡因子为 1(右子树的右子树过高)。
例如:
parent (bf=2)\subR (bf=1)\newNode
4.2.2 旋转原理
与右单旋对称:将subR提升为新的子树根节点,parent变为subR的左孩子,subR的左子树(subRL)变为parent的右子树。
4.2.3 代码实现
void AVLTree<K, V>::RotateL(Node* parent) {// 1. 保存关键节点Node* subR = parent->_right; // parent的右孩子(即将成为新根)Node* subRL = subR->_left; // subR的左子树(即将成为parent的右子树)Node* parentParent = parent->_parent; // parent的父节点// 2. 调整subRL与parent的关系parent->_right = subRL;if (subRL != nullptr) {subRL->_parent = parent;}// 3. 调整parent与subR的关系subR->_left = parent;parent->_parent = subR;// 4. 调整subR与上层节点的关系if (parentParent == nullptr) {// 情况1:parent是原根节点,subR成为新根_root = subR;subR->_parent = nullptr;} else {// 情况2:parent是上层节点的左/右孩子,subR替换其位置if (parentParent->_left == parent) {parentParent->_left = subR;} else {parentParent->_right = subR;}subR->_parent = parentParent;}// 5. 重置平衡因子parent->_bf = 0;subR->_bf = 0;
}
示例图解如下:

4.3 左右双旋(RotateLR):处理 LR 型失衡
4.3.1 失衡场景
LR 型失衡:失衡节点(parent)的平衡因子为 - 2(左子树过高),且其左孩子(subL)的平衡因子为 1(左子树的右子树过高)。
例如:
parent (bf=-2)/subL (bf=1)\subLR (newNode)
4.3.2 旋转原理
左右双旋是 “左单旋 + 右单旋” 的组合:
- 先以
subL为旋转点进行左单旋,将subLR提升为subL的父节点(此时失衡类型转为 LL 型); - 再以
parent为旋转点进行右单旋,将subLR提升为新的子树根节点,恢复平衡。
4.3.3 平衡因子重置逻辑
旋转后平衡因子的重置需根据subLR(双旋的核心节点)插入前的平衡因子判断,分为三种场景:
- 若
subLR->_bf == 0:插入前subLR无孩子,旋转后parent、subL、subLR的平衡因子均为 0; - 若
subLR->_bf == -1:插入前subLR的左子树有节点,旋转后parent->_bf = 1,subL->_bf = 0,subLR->_bf = 0; - 若
subLR->_bf == 1:插入前subLR的右子树有节点,旋转后parent->_bf = 0,subL->_bf = -1,subLR->_bf = 0。
4.3.4 代码实现
void AVLTree<K, V>::RotateLR(Node* parent) {// 1. 保存关键节点Node* subL = parent->_left;Node* subLR = subL->_right;// 记录subLR的平衡因子(用于后续重置)int bf = subLR->_bf;// 2. 第一步:以subL为旋转点进行左单旋(转为LL型)RotateL(subL);// 3. 第二步:以parent为旋转点进行右单旋(恢复平衡)RotateR(parent);// 4. 根据subLR的原始bf重置平衡因子if (bf == 0) {subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;} else if (bf == -1) {// subLR的左子树插入节点,parent的右子树高度增加subL->_bf = 0;subLR->_bf = 0;parent->_bf = 1;} else if (bf == 1) {// subLR的右子树插入节点,subL的左子树高度增加subL->_bf = -1;subLR->_bf = 0;parent->_bf = 0;} else {assert(false); // 异常情况}
}
示例图解如下:



4.4 右左双旋(RotateRL):处理 RL 型失衡
4.4.1 失衡场景
RL 型失衡:失衡节点(parent)的平衡因子为 2(右子树过高),且其右孩子(subR)的平衡因子为 - 1(右子树的左子树过高)。
例如:
parent (bf=2)\subR (bf=-1)/
subRL (newNode)
4.4.2 旋转原理
与左右双旋对称,是 “右单旋 + 左单旋” 的组合:
- 先以
subR为旋转点进行右单旋,将subRL提升为subR的父节点(此时失衡类型转为 RR 型); - 再以
parent为旋转点进行左单旋,将subRL提升为新的子树根节点,恢复平衡。
4.4.3 平衡因子重置逻辑
与左右双旋类似,根据subRL的原始平衡因子判断:
- 若
subRL->_bf == 0:旋转后parent、subR、subRL的平衡因子均为 0; - 若
subRL->_bf == 1:旋转后parent->_bf = -1,subR->_bf = 0,subRL->_bf = 0; - 若
subRL->_bf == -1:旋转后parent->_bf = 0,subR->_bf = 1,subRL->_bf = 0。
4.4.4 代码实现
void AVLTree<K, V>::RotateRL(Node* parent) {// 1. 保存关键节点Node* subR = parent->_right;Node* subRL = subR->_left;// 记录subRL的平衡因子int bf = subRL->_bf;// 2. 第一步:以subR为旋转点进行右单旋(转为RR型)RotateR(subR);// 3. 第二步:以parent为旋转点进行左单旋(恢复平衡)RotateL(parent);// 4. 根据subRL的原始bf重置平衡因子if (bf == 0) {subR->_bf = 0;subRL->_bf = 0;parent->_bf = 0;} else if (bf == 1) {// subRL的右子树插入节点,parent的左子树高度增加subR->_bf = 0;subRL->_bf = 0;parent->_bf = -1;} else if (bf == -1) {// subRL的左子树插入节点,subR的右子树高度增加subR->_bf = 1;subRL->_bf = 0;parent->_bf = 0;} else {assert(false); // 异常情况}
}
示例图解如下:

五、AVL 树的查找实现
AVL 树的查找逻辑与普通二叉搜索树完全一致,因为平衡调整不会破坏二叉搜索树的性质。由于 AVL 树高度为 O (log n),查找时间复杂度也为 O (log n)。
5.1 查找代码实现
template<class K, class V>
typename AVLTree<K, V>::Node* AVLTree<K, V>::Find(const K& key) {Node* cur = _root;while (cur) {if (cur->_kv.first < key) {// 键值比当前节点大,去右子树查找cur = cur->_right;} else if (cur->_kv.first > key) {// 键值比当前节点小,去左子树查找cur = cur->_left;} else {// 找到目标节点,返回指针return cur;}}// 遍历结束未找到,返回nullptrreturn nullptr;
}
代码说明:
- 使用
typename关键字:因为Node是模板类AVLTree<K,V>的嵌套类型,编译器需要明确其为类型(而非成员变量); - 查找逻辑:通过键值比较,不断在左 / 右子树中缩小查找范围,直到找到目标或遍历结束。
5.2 查找示例
以 AVL 树{5,3,7,2,4,6,8}为例,查找键值为 6 的节点:
- 从根节点 5 开始,6>5,去右子树;
- 右子树节点 7,6<7,去左子树;
- 左子树节点 6,键值匹配,返回该节点。
六、AVL 树的平衡检测
为了验证实现的 AVL 树是否正确,需要通过代码检测其平衡性质。平衡检测包括两个核心:
- 任意节点的左右子树高度差绝对值不超过 1;
- 任意节点的平衡因子
_bf等于 “右子树高度 - 左子树高度”(确保平衡因子更新正确)。
6.1 辅助函数:计算节点高度
首先实现_Height函数,递归计算节点的高度(空节点高度为 0,非空节点高度为左右子树最大高度 + 1):
template<class K, class V>
int AVLTree<K, V>::_Height(Node* root) {if (root == nullptr) {return 0;}// 递归计算左、右子树高度int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);// 当前节点高度 = 左右子树最大高度 + 1return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
6.2 平衡检测函数
实现_IsBalanceTree函数,递归检测每个节点的平衡性质:
template<class K, class V>
bool AVLTree<K, V>::_IsBalanceTree(Node* root) {// 空树是AVL树if (root == nullptr) {return true;}// 1. 计算当前节点的实际平衡因子(右高 - 左高)int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);int actualBf = rightHeight - leftHeight;// 2. 检测平衡因子是否异常// 情况1:实际平衡因子绝对值超过1(失衡)if (abs(actualBf) >= 2) {cout << "节点" << root->_kv.first << "高度差异常:实际bf=" << actualBf << endl;return false;}// 情况2:存储的bf与实际bf不相等(平衡因子更新错误)if (root->_bf != actualBf) {cout << "节点" << root->_kv.first << "平衡因子异常:存储bf=" << root->_bf << ",实际bf=" << actualBf << endl;return false;}// 3. 递归检测左、右子树return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}// 公有接口:对外提供平衡检测
template<class K, class V>
bool AVLTree<K, V>::IsBalanceTree() {return _IsBalanceTree(_root);
}
6.3 测试代码
通过插入测试用例,验证 AVL 树的平衡性质:
// 测试用例1:常规场景(包含双旋)
void TestAVLTree1() {AVLTree<int, int> t;// 插入序列:包含LR和RL双旋场景int a[] = {4, 2, 6, 1, 3, 5, 15, 7, 16, 14};for (auto e : a) {t.Insert({e, e});}// 验证二叉搜索树性质:中序遍历应为升序cout << "中序遍历结果:";t.InOrder(); // 预期:1 2 3 4 5 6 7 14 15 16// 验证平衡性质if (t.IsBalanceTree()) {cout << "TestAVLTree1:AVL树平衡检测通过!" << endl;} else {cout << "TestAVLTree1:AVL树平衡检测失败!" << endl;}
}// 测试用例2:大量随机值插入(验证性能与稳定性)
void TestAVLTree2() {const int N = 100000; // 插入10万个随机值vector<int> v;v.reserve(N); // 预分配空间,避免扩容开销// 生成10万个不重复的随机值srand(time(0));for (size_t i = 0; i < N; i++) {v.push_back(rand() + i); // 加i避免重复}// 插入性能测试size_t insertBegin = clock();AVLTree<int, int> t;for (auto e : v) {t.Insert({e, e});}size_t insertEnd = clock();cout << "TestAVLTree2:插入" << N << "个节点耗时:" << (insertEnd - insertBegin) << "ms" << endl;// 平衡检测if (t.IsBalanceTree()) {cout << "TestAVLTree2:AVL树平衡检测通过!" << endl;} else {cout << "TestAVLTree2:AVL树平衡检测失败!" << endl;}// 树高度检测(应接近log2(N) ≈ 17)int height = t._Height(t._root);cout << "TestAVLTree2:AVL树高度为:" << height << endl;// 查找性能测试(查找随机值)size_t findBegin = clock();for (size_t i = 0; i < N; i++) {int key = rand() + i;t.Find(key);}size_t findEnd = clock();cout << "TestAVLTree2:查找" << N << "个随机值耗时:" << (findEnd - findBegin) << "ms" << endl;
}// 主函数:运行测试用例
int main() {TestAVLTree1();cout << "------------------------" << endl;TestAVLTree2();return 0;
}
6.4 测试结果分析
- TestAVLTree1:中序遍历结果为升序,平衡检测通过;
- TestAVLTree2:插入 10 万个节点耗时短,树高度约为 17(符合 O (log n)),查找耗时短,说明 AVL 树性能优异。
七、AVL 树的删除(扩展内容)
本文在这里没有详细实现 AVL 树的删除操作,因为其逻辑是比插入更复杂的:
- 删除节点后,同样需要回溯更新平衡因子;
- 若出现失衡,需通过旋转调整,但旋转后可能导致上层节点继续失衡(插入时旋转后不会影响上层,删除时可能影响);
- 需处理节点的三种删除情况(叶子节点、只有一个孩子、有两个孩子),并结合平衡调整。
若需深入学习,大家可以参考《殷人昆 数据结构:用面向对象方法与 C++ 语言描述》等经典教材。
总结
AVL 树作为经典的自平衡二叉搜索树,通过严格的平衡控制(左右子树高度差≤1)和高效的旋转操作,确保了插入、删除、查找的时间复杂度稳定在 O (log n) 级别,解决了普通二叉搜索树的退化问题。
AVL 树的实现难度主要在于旋转操作的逻辑和平衡因子的更新,建议读者结合代码手动模拟插入和旋转过程,加深理解。在实际工程中,AVL 树适用于对查找性能要求高、插入删除频率适中的场景(若插入删除频率极高,可考虑红黑树,其旋转次数更少)。
希望本文能帮助大家全面掌握 AVL 树的原理与实现,为后续学习更复杂的数据结构(如红黑树)打下坚实的基础!
