AVL 树平衡二叉搜索树
一、AVL 树核心定义
AVL 树是自带平衡约束的二叉搜索树(BST),由 Adelson-Velsky 和 Landis 提出,是最早的自平衡二叉树。
- 满足二叉搜索树性质:左子树所有节点值 < 根节点 < 右子树所有节点值
- 满足平衡因子约束:任意节点左右子树高度差绝对值 ≤ 1
- 平衡因子公式:
平衡因子 = 左子树高度 - 右子树高度- 失衡判定:平衡因子为
2或-2时,树失去平衡,必须旋转修复核心优势
普通 BST 极端情况会退化成链表,查询复杂度O(n);
AVL 树严格控高,查询、插入、删除稳定O(logn)。
二、基础概念:高度与平衡因子
- 叶子节点高度:1
- 空节点高度:0
- 平衡因子取值仅允许:
-1、0、1- 一旦超出范围,触发旋转调整
三、四大失衡场景与旋转修复
插入节点后只会出现四种失衡形态,对应四种旋转操作。
1. LL 左左失衡 → 右旋
- 成因:左子树的左子树插入节点,左偏重
- 操作:以失衡节点左孩子为轴心单次右旋
- 效果:高度回归平衡,保留 BST 有序性
2. RR 右右失衡 → 左旋
- 成因:右子树的右子树插入节点,右偏重
- 操作:以失衡节点右孩子为轴心单次左旋
- 最简修复旋转
3. LR 左右失衡 → 先左后右双旋
- 成因:左孩子的右子树插入节点,左 - 右弯折失衡
- 步骤:
- 对左子节点左旋,转为 LL 形态
- 对根节点右旋,完成平衡
4. RL 右左失衡 → 先右后左双旋
- 成因:右孩子的左子树插入节点,右 - 左弯折失衡
- 步骤:
- 对右子节点右旋,转为 RR 形态
- 对根节点左旋,恢复平衡
四、AVL 树节点结构
AVL树的节点相比普通二叉搜索树(BST),增加了两个关键属性:
_parent:指向父节点的指针。这在插入后回溯更新平衡因子时非常关键。
_bf(Balance Factor):平衡因子。我们定义为右子树高度 - 左子树高度。
cpptemplate<class K, class V> struct AVLTreeNode { pair<K, V> _kv; // 键值对 AVLTreeNode<K, V>* _left; // 左孩子 AVLTreeNode<K, V>* _right; // 右孩子 AVLTreeNode<K, V>* _parent; // 父节点 int _bf; // 平衡因子 (RightHeight - LeftHeight) AVLTreeNode(const pair<K, V>& kv) :_kv(kv) , _left(nullptr) , _right(nullptr) , _parent(nullptr) , _bf(0) { } };
五、 插入逻辑(Insert)
插入分为三个步骤:
按BST规则找到插入位置。
更新祖先节点的平衡因子。
若失衡,进行旋转处理。
cpptemplate<class K, class V> class AVLTree { typedef AVLTreeNode<K, V> Node; public: bool 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 { return false; // 不允许重复键值 } } // 3. 插入新节点 cur = new Node(kv); if (parent->_kv.first < kv.first) { parent->_right = cur; } else { parent->_left = cur; } cur->_parent = parent; // 4. 更新平衡因子并调整 while (parent) { // 更新当前父节点BF if (cur == parent->_left) parent->_bf--; // 左边高了 else parent->_bf++; // 右边高了 // 情况A: BF变为0,说明高度没变,停止更新 if (parent->_bf == 0) { break; } // 情况B: BF为 1 或 -1,需要继续向上回溯 else if (parent->_bf == 1 || parent->_bf == -1) { cur = parent; parent = parent->_parent; } // 情况C: BF为 2 或 -2,出现失衡,需要旋转 else if (parent->_bf == 2 || parent->_bf == -2) { if (parent->_bf == -2 && cur->_bf == -1) // 左左 (LL) { RotateR(parent); } else if (parent->_bf == 2 && cur->_bf == 1) // 右右 (RR) { RotateL(parent); } else if (parent->_bf == -2 && cur->_bf == 1) // 左右 (LR) { RotateLR(parent); } else if (parent->_bf == 2 && cur->_bf == -1) // 右左 (RL) { RotateRL(parent); } else { assert(false); // 理论上不可能走到这里 } break; // 旋转后树已平衡,退出循环 } else { assert(false); // 平衡因子异常 } } return true; }
六、 旋转操作详解
旋转是AVL树的核心。我们需要处理四种情况:
1. 右单旋 (RotateR) ------ 应对 LL 型
当左侧过高,且新增节点在左子树的左侧时发生。
cppvoid RotateR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; Node* parentParent = parent->_parent; // 重新连接节点 parent->_left = subLR; if (subLR) subLR->_parent = parent; subL->_right = parent; parent->_parent = subL; // 处理原parent的父节点链接 if (parentParent == nullptr) // parent是根节点 { _root = subL; subL->_parent = nullptr; } else { if (parent == parentParent->_left) parentParent->_left = subL; else parentParent->_right = subL; subL->_parent = parentParent; } // 更新平衡因子 (右旋后,parent和subL的BF都归零) parent->_bf = subL->_bf = 0; }2. 左单旋 (RotateL) ------ 应对 RR 型
当右侧过高,且新增节点在右子树的右侧时发生。
cppvoid RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; Node* parentParent = parent->_parent; parent->_right = subRL; if (subRL) subRL->_parent = parent; subR->_left = parent; parent->_parent = subR; if (parentParent == nullptr) { _root = subR; subR->_parent = nullptr; } else { if (parent == parentParent->_left) parentParent->_left = subR; else parentParent->_right = subR; subR->_parent = parentParent; } parent->_bf = subR->_bf = 0; }3. 左右双旋 (RotateLR) ------ 应对 LR 型
先对左子树左旋,再对自己右旋。
cppvoid RotateLR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; int bf = subLR->_bf; // 记录插入前的平衡因子 RotateL(parent->_left); // 先左旋左孩子 RotateR(parent); // 再右旋自己 // 根据 subLR 原来的 BF 调整最终平衡因子 if (bf == 0) { subL->_bf = 0; subLR->_bf = 0; parent->_bf = 0; } else if (bf == -1) // 插入在 subLR 的左侧 { subL->_bf = 0; subLR->_bf = 0; parent->_bf = 1; } else if (bf == 1) // 插入在 subLR 的右侧 { subL->_bf = -1; subLR->_bf = 0; parent->_bf = 0; } else { assert(false); } }4. 右左双旋 (RotateRL) ------ 应对 RL 型
先对右子树右旋,再对自己左旋。
cppvoid RotateRL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; int bf = subRL->_bf; RotateR(parent->_right); RotateL(parent); if (bf == 0) { subR->_bf = 0; subRL->_bf = 0; parent->_bf = 0; } else if (bf == 1) { subR->_bf = 0; subRL->_bf = 0; parent->_bf = -1; } else if (bf == -1) { subR->_bf = 1; subRL->_bf = 0; parent->_bf = 0; } else { assert(false); } }
七、 验证与辅助功能
为了确保我们的AVL树是正确的,我们需要编写验证函数。
cpppublic: // 中序遍历 (应该是升序的) void InOrder() { _InOrder(_root); cout << endl; } // 判断是否平衡 bool IsBalanceTree() { return _IsBalanceTree(_root); } private: // 递归检查高度和BF是否匹配 bool _IsBalanceTree(Node* root) { if (root == nullptr) return true; int leftHeight = _Height(root->_left); int rightHeight = _Height(root->_right); // 计算实际的平衡因子 int bf = rightHeight - leftHeight; // 检查:1. 高度差是否合法 2. 存储的BF是否等于实际计算的BF if (abs(bf) >= 2 || bf != root->_bf) { cout << root->_kv.first << " 平衡因子异常" << endl; return false; } return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right); } int _Height(Node* root) { if (root == nullptr) return 0; return max(_Height(root->_left), _Height(root->_right)) + 1; } void _InOrder(Node* root) { if (root == nullptr) return; _InOrder(root->_left); cout << root->_kv.first << " "; _InOrder(root->_right); } private: Node* _root = nullptr; };八、 总结
场景 处理方式 LL 型 (Parent= -2 , Cur= -1) 右单旋 (RotateR) RR 型 (Parent= 2 , Cur= 1) 左单旋 (RotateL) LR 型 (Parent= -2 , Cur= 1) 左右双旋 (RotateLR) RL 型 (Parent= 2 , Cur=- 1) 右左双旋 (RotateRL) 实现AVL树的关键在于理清父子节点之间的指针关系,特别是在旋转过程中,不仅要改孩子指针,还要记得维护
_parent指针。





