一、二叉平衡搜索树定义
1. 标准定义
二叉平衡搜索树(AVL)是在BST 基础上增加平衡约束 的特殊有序二叉树,由 n(n≥0)个节点构成。在完全保留BST有序规则的前提下,通过高度平衡机制限制树的倾斜程度,解决普通BST频繁增删后退化、效率暴跌的问题,始终维持**O(logn)**的稳定操作效率。

2. 核心性质
整棵树需要同时满足BST有序规则与AVL平衡规则,三大硬性性质缺一不可:
- 基础有序:任意节点,其左子树 所有节点关键字 < 当前节点关键字 < 右子树所有节点关键字,节点数值不重复
- 递归平衡:整棵树是AVL树,则所有子树均为AVL树
- 高度平衡:任意节点的左右子树高度差绝对值 ≤ 1
补充考点:AVL树完全继承BST遍历特性,中序 遍历输出序列一定是升序,是区分普通不平衡BST的基础特征。
3. 平衡因子
平衡因子是判定节点是否平衡的核心依据,计算规则:平衡因子 = 左子树高度 - 右子树高度。
合法取值仅有三个:-1、0、1 ;若平衡因子为 ±2 ,代表当前节点失衡,必须通过旋转操作修复平衡。
二、AVL 存储结构
1. 存储设计思路
普通BST二叉链表仅存储孩子指针,无高度记录,无法判定平衡状态,极易退化斜树。本文采用优化二叉链表 实现,舍弃冗余的parent父指针,新增height高度域,通过递归维护节点高度、计算平衡因子,简化旋转与平衡修复逻辑,代码更简洁高效。
2. 结构体定义
分为两层结构体:AVLNode为单个节点单元,存储数据、孩子指针与高度;AVLTree封装根节点指针,统一管理整棵树,适配C++语法规范。
cpp
typedef int ELEMTYPE;
// AVL树节点结构体
struct AVLNode {
ELEMTYPE data;
AVLNode* leftchild;
AVLNode* rightchild;
int height;
// C++构造函数,初始化节点默认值
AVLNode() : data(0), leftchild(nullptr), rightchild(nullptr), height(0) {}
};
// AVL树管理结构体
struct AVLTree {
AVLNode* root;
AVLTree() : root(nullptr) {}
};
3. 存储结构优缺点
优点:无需维护双向父指针,递归实现逻辑简洁;高度域可快速计算平衡因子,精准判定节点失衡;四类旋转操作可完美修复所有失衡场景。
缺点:每次增删节点后,必须回溯更新祖先节点高度、校验平衡状态,相比普通BST,节点维护开销更大。
适配范围:高频查找、动态增删的业务场景,对算法效率稳定性要求高的场景;静态少量数据仅遍历场景,优先使用普通BST。
三、底层工具函数
工具函数为增删改查业务操作提供底层支撑,仅由内部函数调用,不对外暴露,统一采用C++语法实现。
1. BuyNode 创建新节点
实现思路:通过C++ new动态分配节点内存,调用构造函数自动初始化左右孩子指针置空、高度初始化为0,返回新节点地址。
cpp
AVLNode* BuyNode() {
AVLNode* pnewnode = new AVLNode;
return pnewnode;
}
2. Get_Height 获取节点高度
实现思路:空节点无高度,统一定义为**-1**;非空节点返回自身height成员值,为平衡因子计算提供数据支撑。
cpp
int Get_Height(AVLNode* node) {
if (node == nullptr)
return -1;
return node->height;
}
3. Updata_Height 更新节点高度
实现思路:断言校验节点非空,获取左右子树高度,取最大值**+1**,刷新当前节点高度,保证高度数据实时准确。
cpp
void Updata_Height(AVLNode* node) {
assert(node != nullptr);
int lh = Get_Height(node->leftchild);
int rh = Get_Height(node->rightchild);
node->height = lh > rh ? lh + 1 : rh + 1;
}
4. Get_BalanceFactor 获取平衡因子
实现思路:空节点平衡因子默认为0;非空节点通过左右子树高度差值计算,判定节点平衡状态。
cpp
int Get_BalanceFactor(AVLNode* node) {
if (node == nullptr)
return 0;
return Get_Height(node->leftchild) - Get_Height(node->rightchild);
}
5. Left_Rotate 左旋操作
实现思路:适配RR、RL型失衡,将当前失衡节点下沉为右孩子的左子节点,重定向指针后更新相关节点高度。
cpp
AVLNode* Left_Rotate(AVLNode* node) {
assert(node != nullptr);
AVLNode* child = node->rightchild;
AVLNode* grandchild = child->leftchild;
// 完成指针重定向
node->rightchild = grandchild;
child->leftchild = node;
// 旋转后更新高度
Updata_Height(node);
Updata_Height(child);
return child;
}
6. Right_Rotate 右旋操作
实现思路:适配LL、LR型失衡,将当前失衡节点下沉为左孩子的右子节点,重定向指针后更新相关节点高度。
cpp
AVLNode* Right_Rotate(AVLNode* node) {
assert(node != nullptr);
AVLNode* child = node->leftchild;
AVLNode* grandchild = child->rightchild;
// 完成指针重定向
node->leftchild = grandchild;
child->rightchild = node;
// 旋转后更新高度
Updata_Height(node);
Updata_Height(child);
return child;
}
7. Insert_Adjust 插入平衡调整
实现思路:根据节点平衡因子,精准判定四类插入失衡场景,匹配单旋/双旋方案修复平衡;插入失衡仅需单次调整即可恢复全局平衡。
cpp
AVLNode* Insert_Adjust(AVLNode* node) {
int bal = Get_BalanceFactor(node);
// LL型失衡:单次右旋
if (bal == 2 && Get_BalanceFactor(node->leftchild) == 1) {
return Right_Rotate(node);
}
// LR型失衡:先左旋左孩子,再右旋当前节点
if (bal == 2 && Get_BalanceFactor(node->leftchild) == -1) {
node->leftchild = Left_Rotate(node->leftchild);
return Right_Rotate(node);
}
// RR型失衡:单次左旋
if (bal == -2 && Get_BalanceFactor(node->rightchild) == -1) {
return Left_Rotate(node);
}
// RL型失衡:先右旋右孩子,再左旋当前节点
if (bal == -2 && Get_BalanceFactor(node->rightchild) == 1) {
node->rightchild = Right_Rotate(node->rightchild);
return Left_Rotate(node);
}
return node;
}
8. Delete_Adjust 删除平衡调整
实现思路:兼容删除连锁失衡特性,相比插入调整,平衡因子为0时仍需修复,覆盖所有删除失衡场景,逐层回溯修复祖先节点失衡。
cpp
AVLNode* Delete_Adjust(AVLNode* node) {
int bal = Get_BalanceFactor(node);
// LL、L平衡型
if (bal == 2) {
if (Get_BalanceFactor(node->leftchild) == 1 || Get_BalanceFactor(node->leftchild) == 0) {
return Right_Rotate(node);
}
if (Get_BalanceFactor(node->leftchild) == -1) {
node->leftchild = Left_Rotate(node->leftchild);
return Right_Rotate(node);
}
}
// RR、R平衡型
if (bal == -2) {
if (Get_BalanceFactor(node->rightchild) == -1 || Get_BalanceFactor(node->rightchild) == 0) {
return Left_Rotate(node);
}
if (Get_BalanceFactor(node->rightchild) == 1) {
node->rightchild = Right_Rotate(node->rightchild);
return Left_Rotate(node);
}
}
return node;
}
四、AVL 五大基础操作
1. 初始化二叉平衡树
实现思路:断言校验树结构体指针合法,将树根指针置空,初始化空的AVL树。
cpp
void Init_AVLTree(AVLTree* pTree) {
assert(pTree != nullptr);
pTree->root = nullptr;
}
2. 插入节点
实现思路:遵循BST有序规则查找插入位置,递归创建新节点;回溯过程更新节点高度,校验并修复失衡,保证插入后树始终平衡。
递归核心逻辑:
- 空节点位置新建节点,作为叶子节点插入
- 数值重复直接返回,AVL树继承BST数值唯一规则
- 递归回溯更新每层节点高度
- 调用插入调整函数修复失衡
cpp
// 递归插入底层实现
AVLNode* Insert_Recursion(AVLNode* root, ELEMTYPE val) {
// 找到插入位置,新建节点
if (root == nullptr) {
AVLNode* pnewnode = BuyNode();
pnewnode->data = val;
return pnewnode;
}
// 按BST规则递归插入
if (val > root->data) {
root->rightchild = Insert_Recursion(root->rightchild, val);
}
else if (val < root->data) {
root->leftchild = Insert_Recursion(root->leftchild, val);
}
else {
// 数值重复,插入失败
return root;
}
// 更新当前节点高度并修复平衡
Updata_Height(root);
return Insert_Adjust(root);
}
// 对外插入接口
bool Insert_AVL(AVLTree* pTree, ELEMTYPE val) {
assert(pTree != nullptr);
pTree->root = Insert_Recursion(pTree->root, val);
return true;
}
3. 递归查找节点
实现思路:完全复用BST二分查找逻辑,从根节点出发,依据数值大小规则遍历左右子树,匹配返回节点,无匹配返回空。
cpp
AVLNode* Search_AVL(AVLNode* root, ELEMTYPE val) {
if (root == nullptr)
return nullptr;
if (val > root->data) {
return Search_AVL(root->rightchild, val);
}
else if (val < root->data) {
return Search_AVL(root->leftchild, val);
}
else {
return root;
}
}
4. 删除节点
(1)三类删除场景对比
零孩子(叶子节点):直接释放节点,无后续指针维护,逻辑最简单。
单孩子(仅左/右子树):唯一子节点直接顶替待删节点位置,继承父节点关联。
双孩子(左右子树均存在) :无法直接删除,采用狸猫换太子方案,转换为零/单分支删除场景。
(2)狸猫换太子核心原理
AVL树继承BST节点特性,任意节点的直接前驱/后继最多仅有单侧子树。删除双分支节点时,取**左子树最大值(直接前驱)**覆盖待删节点数据,将删除目标转移至前驱节点,规避复杂的双分支替换逻辑。
(3)AVL删除核心特点
普通BST删除无需平衡调整,而AVL树删除节点会导致祖先链连锁失衡,必须从删除位置回溯至根节点,逐层更新高度、修复平衡。
cpp
// 递归删除底层实现
AVLNode* Delete_Recursion(AVLNode* root, ELEMTYPE val) {
if (root == nullptr)
return nullptr;
// 递归查找待删除节点
if (val > root->data) {
root->rightchild = Delete_Recursion(root->rightchild, val);
}
else if (val < root->data) {
root->leftchild = Delete_Recursion(root->leftchild, val);
}
else {
// 找到待删除节点,处理三类删除场景
if (root->leftchild != nullptr && root->rightchild != nullptr) {
// 双分支:狸猫换太子,替换前驱节点
AVLNode* cat = root->leftchild;
while (cat->rightchild != nullptr)
cat = cat->rightchild;
root->data = cat->data;
root->leftchild = Delete_Recursion(root->leftchild, cat->data);
}
else {
// 零/单分支节点
AVLNode* child = root->leftchild != nullptr ? root->leftchild : root->rightchild;
delete root;
root = nullptr;
return child;
}
}
// 回溯更新高度、修复平衡
Updata_Height(root);
return Delete_Adjust(root);
}
// 对外删除接口
bool Delete_AVL(AVLTree* pTree, ELEMTYPE val) {
assert(pTree != nullptr);
pTree->root = Delete_Recursion(pTree->root, val);
return true;
}
5. 中序遍历打印
实现思路:采用非递归单栈法模拟递归遍历,遵循左根右顺序,依托AVL树BST有序特性,遍历结果严格升序。
cpp
void Show_InOrder_AVL(AVLNode* root) {
assert(root != nullptr);
std::stack<AVLNode*> st;
st.push(root);
bool tag = true;
while (!st.empty()) {
while (tag && st.top()->leftchild != nullptr) {
st.push(st.top()->leftchild);
}
AVLNode* tmp = st.top();
st.pop();
printf("%d ", tmp->data);
if (tmp->rightchild != nullptr) {
st.push(tmp->rightchild);
tag = true;
}
else {
tag = false;
}
}
printf("\n");
}
五、AVL 失衡类型
1. 四大失衡场景与修复方案

LL型失衡 :左子树高度过高,左孩子左子树偏重,采用单次右旋修复。
LR型失衡 :左子树高度过高,左孩子右子树偏重,采用先左旋、后右旋双旋修复。
RR型失衡 :右子树高度过高,右孩子右子树偏重,采用单次左旋修复。
RL型失衡 :右子树高度过高,右孩子左子树偏重,采用先右旋、后左旋双旋修复。
2. 插入与删除平衡核心差异
插入操作 :新增节点仅会造成单条祖先链一处失衡,一次旋转即可全局平衡,无需多层回溯。
删除操作 :节点删除会改变子树高度,引发多层祖先节点连锁失衡,必须逐层回溯至根节点,多次校验修复。
六、总结
1. 结构本质区别
普通二叉树无数值约束,仅限制节点度≤2;BST具备有序规则但无平衡约束,易退化斜树;AVL树基于BST拓展高度平衡机制,在有序基础上严格限制子树高度差,彻底解决树结构退化问题。
2. 遍历特性区别
普通二叉树所有遍历序列无序;BST与AVL树遍历特性一致,仅中序遍历输出严格升序,先、后序遍历无有序特性,无法体现有序属性。
3. 性能与工程选型
普通BST效率不稳定,平衡状态O(logn)、斜树退化O(n);AVL树全程维持平衡形态,增删查稳定O(logn)。刷题简单遍历场景可选普通BST,高频动态增删、高稳定性检索场景优先选用AVL树。
三类树形核心对比表
| 对比维度 | 普通二叉树 | 二叉搜索树(BST) | AVL平衡搜索树 |
|---|---|---|---|
| 节点规则 | 无数值大小约束 | 左小右大、数值不重复 | 满足BST规则+高度平衡 |
| 平衡特性 | 无平衡约束 | 无平衡,易斜树退化 | 高度差≤1,严格平衡 |
| 中序遍历 | 序列无序 | 严格升序 | 严格升序 |
| 操作效率 | O(n) | O(logn)~O(n) | 稳定O(logn) |
| 核心用途 | 简单层级存储、遍历 | 基础排序、动态检索 | 高性能、稳定数据检索 |