1. AVL树的概念与原理
AVL树是最早发明的自平衡二叉查找树,由前苏联科学家G. M. Adelson-Velsky和E. M. Landis在1962年的论文《An algorithm for the organization of information》中提出。AVL树得名于两位发明者的姓氏首字母。
1.1 AVL树的定义
AVL树是一棵空树,或者是具有以下性质的二叉搜索树:
- 它的左右子树都是AVL树
- 左右子树的高度差的绝对值不超过1
AVL树通过严格控制高度差来维持平衡,是一种高度平衡的二叉搜索树。
1.2 平衡因子
在AVL树的实现中,我们引入平衡因子(Balance Factor)的概念:
- 每个节点都有一个平衡因子
- 平衡因子 = 右子树高度 - 左子树高度
- 合法平衡因子值:-1, 0, 1
平衡因子虽然并非AVL树必须的,但它能直观地反映树的平衡状态,便于我们观察和控制树的平衡性。
1.3 为什么高度差不超过1?
初学者可能会问:为什么不要求高度差为0,这样不是更平衡吗?通过分析可以发现:
- 在某些情况下(如节点数为2、4等),无法做到高度差为0
- 允许1的高度差可以在平衡性和实现复杂度之间取得良好平衡
1.4 AVL树的性能
AVL树的节点分布与完全二叉树类似,高度始终控制在O(logN),因此其增删查改操作的时间复杂度都能保证在O(logN),相比普通二叉搜索树有本质提升。


2. AVL树的实现
2.1 AVL树的结构
template<class K, class V>
struct AVLTreeNode {
pair<K, V> _kv;
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent; // 需要parent指针来更新平衡因子
int _bf; // balance factor
AVLTreeNode(const pair<K, V>& kv)
:_kv(kv)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _bf(0)
{}
};
template<class K, class V>
class AVLTree {
typedef AVLTreeNode<K, V> Node;
public:
// 接口函数
private:
Node* _root = nullptr;
};
2.2 AVL树的插入操作
插入操作分为几个关键步骤:
- 按二叉搜索树规则插入新节点
- 更新平衡因子:从新节点向上更新到根节点路径上的平衡因子
- 检查平衡性:如果发现不平衡,进行旋转调整
- 旋转调整:通过旋转恢复平衡,同时降低子树高度
2.2.1 平衡因子更新规则
- 平衡因子 = 右子树高度 - 左子树高度
- 只有子树高度变化才会影响当前节点的平衡因子
- 插入节点后平衡因子更新:
- 新节点在父节点右子树:父节点平衡因子++
- 新节点在父节点左子树:父节点平衡因子–
2.2.2 更新停止条件
- 平衡因子变为0:说明原本一边高一边低的子树现在两边高度相同,子树高度不变,更新结束
- 平衡因子变为1或-1:说明原本平衡的子树现在一边高一边低,子树高度增加1,需要继续向上更新
- 平衡因子变为2或-2:说明子树已不平衡,需要进行旋转调整,旋转后更新结束
- 更新到10结点,平衡因子为2,10所在的子树已经不平衡,需要旋转处理

- 更新到中间结点,3为根的子树高度不变,不会影响上一层,更新结束

- 最坏更新到根停止

2.2.3 插入实现代码
bool Insert(const pair<K, V>& kv) {
if (_root == nullptr) {
_root = new Node(kv);
return true;
}
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;
}
}
cur = new Node(kv);
if (parent->_kv.first < kv.first) {
parent->_right = cur;
}
else {
parent->_left = cur;
}
cur->_parent = parent;
// 更新平衡因子
while (parent) {
if (cur == parent->_left) {
parent->_bf--;
}
else {
parent->_bf++;
}
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) {
// 不平衡,需要旋转处理
// ...旋转代码将在下面介绍
break;
}
else {
assert(false);
}
}
return true;
}
2.3 旋转操作
当树失去平衡时,需要通过旋转来恢复平衡。旋转操作有四种基本类型:
2.3.1 旋转原则
- 保持二叉搜索树的性质不变
- 使不平衡的树恢复平衡
- 降低旋转子树的高度
2.3.2 右单旋
适用场景:左子树过高(平衡因子为-2),且左子树的左子树较高(左子树的平衡因子为-1)
操作步骤:
- 将父节点的左子节点提升为新根
- 将新根的右子树作为原父节点的左子树
- 将原父节点作为新根的右子树
- 简记:右单旋就是把右边压下去(对照图理解,左单旋同理)

void RotateR(Node* parent) {
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR) subLR->_parent = parent;
Node* parentParent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parentParent == nullptr) {
_root = subL;
subL->_parent = nullptr;
}
else {
if (parent == parentParent->_left) {
parentParent->_left = subL;
}
else {
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
parent->_bf = subL->_bf = 0;
}
2.3.3 左单旋
适用场景:右子树过高(平衡因子为2),且右子树的右子树较高(右子树的平衡因子为1)
操作步骤:
- 将父节点的右子节点提升为新根
- 将新根的左子树作为原父节点的右子树
- 将原父节点作为新根的左子树

void RotateL(Node* parent) {
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL) subRL->_parent = parent;
Node* parentParent = 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;
}
2.3.4 左右双旋
适用场景:左子树过高(平衡因子为-2),且左子树的右子树较高(左子树的平衡因子为1)
操作步骤:
- 先对左子节点进行左旋
- 再对父节点进行右旋
- 三个场景如图

void RotateLR(Node* parent) {
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(parent->_left);
RotateR(parent);
// 根据subLR原来的平衡因子调整各节点平衡因子
if (bf == 0) {
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 0;
}
else if (bf == -1) {
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 1;
}
else if (bf == 1) {
subL->_bf = -1;
subLR->_bf = 0;
parent->_bf = 0;
}
else {
assert(false);
}
}
2.3.5 右左双旋
适用场景:右子树过高(平衡因子为2),且右子树的左子树较高(右子树的平衡因子为-1)
操作步骤:
- 先对右子节点进行右旋
- 再对父节点进行左旋
- 同上
void RotateRL(Node* parent) {
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
// 根据subRL原来的平衡因子调整各节点平衡因子
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);
}
}
2.4 AVL树的查找
查找操作与普通二叉搜索树相同,时间复杂度为O(logN):
Node* 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;
}
}
return nullptr;
}
2.5 AVL树的平衡检测
为了验证AVL树的实现是否正确,我们可以通过检查每个节点的平衡因子和子树高度差来进行验证:
int _Height(Node* root) {
if (root == nullptr) return 0;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return max(leftHeight, rightHeight) + 1;
}
bool _IsBalanceTree(Node* root) {
if (root == nullptr) return true;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
int diff = rightHeight - leftHeight;
if (abs(diff) >= 2) {
cout << root->_kv.first << "高度差异常" << endl;
return false;
}
if (root->_bf != diff) {
cout << root->_kv.first << "平衡因子异常" << endl;
return false;
}
return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}
3.总代码实现
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace sty {
// AVL树节点结构体
template<class K, class V>
struct AVLTreeNode {
pair<K, V> _kv; // 键值对
AVLTreeNode<K, V>* _left; // 左子节点
AVLTreeNode<K, V>* _right; // 右子节点
AVLTreeNode<K, V>* _parent; // 父节点指针(用于平衡因子更新)
int _bf; // 平衡因子(balance factor)
// 构造函数
AVLTreeNode(const pair<K, V>& kv)
:_kv(kv)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _bf(0)
{}
};
// AVL树类
template<class K, class V>
class AVLTree {
typedef AVLTreeNode<K, V> Node;
public:
// 插入键值对
bool Insert(const pair<K, V>& kv) {
// 如果树为空,直接创建根节点
if (_root == nullptr) {
_root = new Node(kv);
return true;
}
// 查找插入位置
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;
}
}
// 创建新节点并插入
cur = new Node(kv);
if (parent->_kv.first < kv.first) {
parent->_right = cur;
}
else {
parent->_left = cur;
}
cur->_parent = parent;
// 更新平衡因子
while (parent) {
// 根据插入位置更新父节点的平衡因子
if (cur == parent->_left)
parent->_bf--; // 插入在左子树,平衡因子减1
else
parent->_bf++; // 插入在右子树,平衡因子加1
// 根据更新后的平衡因子决定下一步操作
if (parent->_bf == 0) {
// 平衡因子为0,说明子树高度不变,更新结束
break;
}
else if (parent->_bf == 1 || parent->_bf == -1) {
// 平衡因子为1或-1,说明子树高度变化,需要继续向上更新
cur = parent;
parent = parent->_parent;
}
else if (parent->_bf == 2 || parent->_bf == -2) {
// 平衡因子为2或-2,需要进行旋转调整
if (parent->_bf == -2 && cur->_bf == -1) {
// 左左情况 - 右单旋
RotateR(parent);
}
else if (parent->_bf == 2 && cur->_bf == 1) {
// 右右情况 - 左单旋
RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1) {
// 左右情况 - 左右双旋
RotateLR(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1) {
// 右左情况 - 右左双旋
RotateRL(parent);
}
else {
assert(false); // 不应该出现的情况
}
break; // 旋转后子树高度恢复,更新结束
}
else {
assert(false); // 平衡因子异常
}
}
return true;
}
// 右单旋(处理左左不平衡情况)
void RotateR(Node* parent) {
Node* subL = parent->_left; // parent的左孩子
Node* subLR = subL->_right; // subL的右孩子
// 将subLR作为parent的左孩子
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* pParent = parent->_parent; // parent的父节点
// 将parent作为subL的右孩子
subL->_right = parent;
parent->_parent = subL;
// 处理旋转后的根节点连接
if (parent == _root) {
_root = subL;
subL->_parent = nullptr;
}
else {
if (pParent->_left == parent) {
pParent->_left = subL;
}
else {
pParent->_right = subL;
}
subL->_parent = pParent;
}
// 更新平衡因子
subL->_bf = 0;
parent->_bf = 0;
}
// 左单旋(处理右右不平衡情况)
void RotateL(Node* parent) {
Node* subR = parent->_right; // parent的右孩子
Node* subRL = subR->_left; // subR的左孩子
// 将subRL作为parent的右孩子
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* parentParent = parent->_parent; // parent的父节点
// 将parent作为subR的左孩子
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;
}
// 左右双旋(处理左右不平衡情况)
void RotateLR(Node* parent) {
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf; // 保存旋转前的平衡因子
// 先对subL进行左旋,再对parent进行右旋
RotateL(parent->_left);
RotateR(parent);
// 根据subLR原来的平衡因子调整各节点平衡因子
if (bf == -1) { // 新节点插入在subLR的左子树
subLR->_bf = 0;
subL->_bf = 0;
parent->_bf = 1;
}
else if (bf == 1) { // 新节点插入在subLR的右子树
subLR->_bf = 0;
subL->_bf = -1;
parent->_bf = 0;
}
else if (bf == 0) { // subLR本身就是新节点
subLR->_bf = 0;
subL->_bf = 0;
parent->_bf = 0;
}
else {
assert(false); // 不应该出现的情况
}
}
// 右左双旋(处理右左不平衡情况)
void RotateRL(Node* parent) {
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf; // 保存旋转前的平衡因子
// 先对subR进行右旋,再对parent进行左旋
RotateR(parent->_right);
RotateL(parent);
// 根据subRL原来的平衡因子调整各节点平衡因子
if (bf == 0) { // subRL本身就是新节点
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1) { // 新节点插入在subRL的右子树
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1) { // 新节点插入在subRL的左子树
subR->_bf = 1;
subRL->_bf = 0;
parent->_bf = 0;
}
else {
assert(false); // 不应该出现的情况
}
}
// 中序遍历
void InOrder() {
_InOrder(_root);
cout << endl;
}
// 获取树的高度
int Height() {
return _Height(_root);
}
// 获取树的节点数量
int Size() {
return _Size(_root);
}
// 检查树是否平衡
bool IsBalanceTree() {
return _IsBalanceTree(_root);
}
// 查找键为key的节点
Node* 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;
}
}
return nullptr;
}
private:
// 递归中序遍历
void _InOrder(Node* root) {
if (root == nullptr) {
return;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
// 递归计算树的高度
int _Height(Node* root) {
if (root == nullptr)
return 0;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
// 递归计算树的节点数量
int _Size(Node* root) {
if (root == nullptr)
return 0;
return _Size(root->_left) + _Size(root->_right) + 1;
}
// 递归检查树是否平衡
bool _IsBalanceTree(Node* root) {
// 空树是平衡的
if (nullptr == root)
return true;
// 计算当前节点的平衡因子(左右子树高度差)
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
int diff = rightHeight - leftHeight;
// 检查平衡因子是否合法
if (abs(diff) >= 2) { // 高度差超过1
cout << root->_kv.first << "高度差异常" << endl;
return false;
}
// 检查存储的平衡因子是否与实际计算的一致
if (root->_bf != diff) {
cout << root->_kv.first << "平衡因子异常" << endl;
return false;
}
// 递归检查左右子树
return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}
private:
Node* _root = nullptr; // 根节点
};
}
921

被折叠的 条评论
为什么被折叠?



