本文深入解析了AVL树的核心概念与实现,包括节点结构设计、平衡因子定义及其更新机制、插入操作的自下而上平衡调整策略,以及四种旋转方式(左单旋、右单旋、左右双旋、右左双旋)对保持树平衡的重要作用。同时,提供了AVL树高度计算与平衡检测的实现方法,确保每个节点的平衡因子正确维护,从而保证树在插入操作后的高效性与稳定性。通过本文内容,读者可以系统掌握AVL树的原理、实现与调试技巧,为高性能二叉搜索树的应用打下坚实基础。

BF = height(right_subtree) - height(left_subtree)虽然AVL树的平衡因子并非必需,但它为我们提供了一种方便的方式来判断树是否平衡,就像一个“风向标”,帮助我们轻松判断是否需要进行旋转操作来恢复平衡。
O(log n)。这使得在AVL树上进行增、删、查、改等操作的时间复杂度都为O(log n),比普通的二叉搜索树(BST)效率要高得多。我们通过采用类模板的形式来实现VAL树,并且使用pair来储存键对值;所以我们需要引入头文件<utility>,并且展开命名空间std,以便于使用pair类型。
为了高效访问节点成员,我们使用
struct来定义AVL树的节点结构体。每个节点包括以下成员:
pair(K , V)): 存储节点的数据。left,right):指向左右子树的指针,用于我们构建二叉搜索树的结构。balanceFactor):用于个衡量树的平衡性。当节点平衡因子为1 或0或-1的时候,树是平衡的,超过这个范围则需要通过旋转操作来恢复平衡。parent):用于指向当前节点的父节点,帮助进行旋转操作时的节点链接。这种设计使得节点的结构呈现出三叉链结构,即每个节点不仅有左右子树的指针,还有指向父亲节点的指针。这种结构方便我们在旋转操作的时候直接访问父节点,从而进行必要的树的结构调整。
// 节点结构体模板
template<class K, class V>
struct AVLTreeNode {
pair<K, V> _data; // 存储节点的键值对
AVLTreeNode* _left; // 左子树指针
AVLTreeNode* _right; // 右子树指针
AVLTreeNode* _parent; // 父节点指针
int _bf; // 平衡因子(Balance Factor)
// 构造函数:初始化节点数据与指针
AVLTreeNode(const pair<K, V>& data)
: _data(data)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _bf(0)
{}
};在实现AVL树的类模板的时候,我们为AVLTreeNode类型定义了一个简化的别名Node,这样可以避免在代码中多次重复的使用长类型名称。接下来我们定义AVL的构造函数,根节点初始化为空。
parent指针默认为nullptr。为了防止外部访问根节点,我们将根节点设置成私有成员。// AVL树模板类
template<class K, class V>
class AVLTree {
private:
using Node = AVLTreeNode<K, V>; // 使用别名简化节点类型书写
Node* _root; // 树的根节点指针
public:
// 构造函数:初始化根节点为空
AVLTree()
: _root(nullptr)
{}
// 析构函数(后续可实现)
~AVLTree() = default;
// 其他成员函数(插入、旋转、平衡调整等)后续实现
};Balance Factor,简称BF)本质上是一个int变量,用于判断AVL树中某个节点是否需要进行旋转以保持树的平衡。每当节点的插入或者删除时,平衡因子都会随之更新,用来实时反映当前节点的平衡状态。
计算方法:平衡因子 = 右子树高度 - 左子树高度(-1,0,1):
BF = 0,左右子树的高度相等,树处于完全平衡状态BF = 1,右子树比左子树高一层,轻微右倾斜BF = -1,左子树比右子树高一层,轻微左倾斜
BF = 2 或BF = -2 ),就说明该节点已经失衡,不再满足AVL树的平衡条件,需要通过旋转操作去恢复平衡。在AVL树的插入操作中,我们在插入新节点后需要从插入点开始,自下而上的更新各祖先节点的平衡因子。根据平衡因子的变化来决定树是否需要继续更新或者是否需要旋转。
①parent更新后平衡因子 = 0(
BF = 0)的情况:

当更新的过程中,parent的平衡因子由 -1-> 或者1->0,parent的左右子树高度差为1,即一边高一边低;而新节点被插入在了较低的一边,使得两边重新到达了平衡。此时,parent所在的子树的整体高度并没有发生变化,,因此不会影响更高层父节点的平衡性。即更新到此结束,无需继续向上更新。
②parent更新后平衡因子 = 1 / -1(
BF = 正负1)的情况:

当parent的平衡因子由0->1或0->-1,表示在插入前,左右子树高度相同;而插入新节点后,导致其中的一边高度增加1,parent变得一高一低。尽管此时parent子树任然保持平衡(平衡因子的绝对值小于等于1),但是该子树的高度整体增加了1,这样高度变化将传递给更高层的父节点。即需要继续向上更新祖先节点的平衡因子。
③parent更新后平衡因子 = 2 / -2(
BF = 正负2)的情况:

当parent的平衡因子由1->2或**-1->-2**,表示插入节点出现在了左右子树高的一边,导致孩子更加失衡;此时AVL的平衡性被破坏,必须进行旋转操作来恢复平衡。
④ 更新到根节点的终止条件: 在不断向上更新的过程中,如果最终更新到了根节点,且根的平衡因子为 ±1,说明树依旧平衡,插入操作至此完成。若在更新过程中触发了旋转,则更早地已经结束更新。即当根平衡或局部旋转完成后,插入更新终止。
//插入
bool Insert(const pair<K, V>& data)
{
//如果根节点是空,直接new一个新的当作跟节点并返回正确
if (_root == nullptr)
{
_root = new Node(data);
return true;
}
//如果根节点不是空,按照插入规则查找到需要插入节点的位置
else
{
Node* parent = nullptr;
Node* cur = _root;
while (cur != nullptr)
{
if (data.first > cur->_data.first)//插入的节点的key值大于当前节点的key值
{
parent = cur;
cur = cur->_right;
}
else if (data.first < cur->_data.first)//插入的节点的key值小于当前节点的key值
{
parent = cur;
cur = cur->_left;
}
else { return false; }//插入的节点的key值等于当前节点的key值,返回错误
}
cur = new Node(data);//找到需要插入新节点的位置,new出来
cur->_parent = parent;//三叉链的结构需要我们在创建新节点后重新链接父节点
//判断新插入节点是在父节点的左边还是右边
if (cur->_data.first > parent->_data.first) { parent->_right = cur; }
else { parent->_left = cur; }
//更新平衡因子
while (parent != nullptr)
{
if (cur == parent->_right) { parent->_bf++; }
else { parent->_bf--; }
// 如果 parent->_bf == 0,说明当前子树重新恢复平衡,高度未变化,停止更新
if (parent->_bf == 0) { break; }
else if (parent->_bf == 1 || parent->_bf == -1)
{
cur = parent;
parent = parent->_parent;
}
else if (parent->_bf == 2 || parent->_bf == -2)
{
//失衡节点右子树高且它的右孩子的右子树高,只需要进行左单旋
if (parent->_bf == 2 && cur->_bf == 1) { RotateL(parent); }
//失衡节点左子树高且它的左孩子的左子树高,只需要进行右单旋
else if (parent->_bf == -2 && cur->_bf == -1) { RotateR(parent); }
//失衡节点右子树高且它的右孩子的左子树高,需要进行右左单旋
else if (parent->_bf == 2 && cur->_bf == -1) { RotateRL(parent); }
//失衡节点左子树高且它的左孩子的右子树高,需要进行左右单旋
else if (parent->_bf == -2 && cur->_bf == 1) { RotateLR(parent); }
//如果cur的平衡因子不符合为1或-1的情况,说明在插入前树已经不是AVL树直接断言错误即可
else { assert(false); }
break;
}
//如果parent的平衡因子不符合为0,1或-1,2或-2的情况说明在插入前树已经不是AVL树了,直接断言错误即可
else { assert(false); }
}
return true;
}
}
a<5<b<10,我们可以选取中间节点5来充当新的平衡中枢:经过右单旋后,整棵树依然符合二叉搜索树的有序性规则,同时恢复了AVL的平衡性。此前该局部子树的高度重新与之前保持一致。

//右单旋,此时的parent是失衡节点
void RotateR(Node* parent)
{
Node* ppNode = parent->_parent;
Node* cur = parent->_left;
Node* curR = cur->_right;
parent->_left = curR;
if (curR) { curR->_parent = parent; }
cur->_right = parent;
parent->_parent = cur;
//旋转后parent是根节点
if (ppNode == nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else//旋转后parent不是根节点
{
if (ppNode->_left == parent)
{
ppNode->_left = cur;
cur->_parent = ppNode;
}
else
{
ppNode->_right = cur;
cur->_parent = ppNode;
}
}
parent->_bf = cur->_bf = 0;
}
a>15>b>c,我们可以选取中间节点15来充当新的平衡中枢:经过右单旋后,整棵树依然符合二叉搜索树的有序性规则,同时恢复了AVL的平衡性。此前该局部子树的高度重新与之前保持一致。

void RotateL(Node* parent)
{
Node* ppNode = parent->_parent;
Node* cur = parent->_right;
Node* curL = cur->_left;
parent->_right = curL;
if (curL) { curL->_parent = parent; }
cur->_left = parent;
parent->_parent = cur;
if (ppNode == nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppNode->_left == parent)
{
ppNode->_left = cur;
cur->_parent = ppNode;
}
else
{
ppNode->_right = cur;
cur->_parent = ppNode;
}
}
parent->_bf = cur->_bf = 0;
}




//左右双旋是失衡节点的左子树过高,但是它的左孩子cur的右子树过高
void RotateLR(Node* parent)
{
Node* cur = parent->_left;
Node* curR = cur->_right;
int bf = curR->_bf;
RotateL(cur);
RotateR(parent);
//此时插入的节点就是curR
if (bf == 0)
{
parent->_bf = 0;
cur->_bf = 0;
curR->_bf = 0;
}
else if (bf == -1)//此时插入的节点是在curR的左子树里
{
parent->_bf = 1;
cur->_bf = 0;
curR->_bf = 0;
}
else if(bf == 1)//此时说明插入的节点是在curR的右子树里
{
parent->_bf = 0;
cur->_bf = -1;
curR->_bf = 0;
}
else { assert(false); }
}右左双旋(RL型)发生在节点的右子树过高,但其右孩子的左子树高度增加时产生的复合型失衡。以节点 10 为例,当新节点插入到其右子树的左侧(即 b 子树)时,虽然 10 的右子树整体更高,但其右孩子 15 的左子树比右子树高,这种情况下仅靠左或右单旋都无法恢复整棵树的平衡。
解决方法分为两个步骤:
为了更精确地分析平衡因子变化,可将 b 子树进一步拆解为高度为 h-1 的 e 和 f 子树,以及新插入节点 12。根据新增节点的位置不同,平衡因子的更新也不同,可分为三种场景:
通过以上步骤,右左双旋能够有效解决右子树左高导致的复合型失衡,保证 AVL 树在插入节点后仍然保持平衡。

//右左双旋是失衡节点的右子树过高,但是它的右孩子cur的左子树过高
void RotateRL(Node* parent)
{
Node* cur = parent->_right;
Node* curL = cur->_left;
int bf = curL->_bf;
RotateR(cur);
RotateL(parent);
//此时插入的节点就是curL
if (bf == 0)
{
parent->_bf = 0;
cur->_bf = 0;
curL->_bf = 0;
}
else if (bf == -1)//此时插入的节点是在curL的左子树里
{
parent->_bf = 0;
cur->_bf = 1;
curL->_bf = 0;
}
else if(bf == 1)//此时说明插入的节点是在curL的右子树里
{
parent->_bf = -1;
cur->_bf = 0;
curL->_bf = 0;
}
else { assert(false); }
}public:
int Height()
{
return Height(_root);
}Height(Node* root),传入根节点 _root 作为参数。private:
int Height(Node* root)
{
if (root == nullptr)
{
return 0;
}
int heightL = Height(root->_left);
int heightR = Height(root->_right);
return heightL > heightR ? heightL + 1 : heightR + 1;
}if (root == nullptr)
{
return 0;
}int heightL = Height(root->_left);
int heightR = Height(root->_right);return heightL > heightR ? heightL + 1 : heightR + 1;public:
bool Isbalance()
{
return Isbalance(_root);
}Isbalance(Node* root),从根节点开始。private:
bool Isbalance(Node* root)
{
if (root == nullptr)
{
return true;
}
Node* left = root->_left;
Node* right = root->_right;
int heightL = Height(left);
int heightR = Height(right);
int bf = heightR - heightL;
if (bf != root->_bf)
{
cout << "平衡因子异常" << root->_data.first << ':' << root->_bf << endl;
return false;
}
return abs(bf) <= 1 && Isbalance(left) && Isbalance(right);
}if (root == nullptr)
{
return true;
}Node* left = root->_left;
Node* right = root->_right;int heightL = Height(left);
int heightR = Height(right);
int bf = heightR - heightL;bf 是当前节点的实际平衡因子。if (bf != root->_bf)
{
cout << "平衡因子异常" << root->_data.first << ':' << root->_bf << endl;
return false;
}bf 与节点 _bf 成员值不一致,则说明树的平衡因子维护出错。return abs(bf) <= 1 && Isbalance(left) && Isbalance(right);AVL树通过限制节点左右子树高度差并引入平衡因子,实现了自平衡二叉搜索树。在插入节点时,平衡因子自下而上更新,并在必要时通过旋转操作恢复平衡,从而确保树的高度始终为O(log n),保证增、删、查等操作效率高。本文通过结构化代码示例和旋转原理解析,使读者不仅理解AVL树的理论基础,更能掌握实际实现与调试方法,为构建高效的数据结构提供完整指导。