目录
AVL树的简介
AVL节点的构建
AVL树体的构建
具体片段解析
旋转算法
AVL树的验证
AVL树的简介
AVL树是一种自平衡的二叉搜索树,它在19世纪60年代由Adelson-Velsky和Landis首次提出。在AVL树中,任何节点的两个子树的高度最大差别为1,这种平衡确保了树的操作(如插入、删除和查找)的时间复杂度为O(log n)。下面是AVL树在C++中的基本概念:
基本概念:
- 节点:AVL树的每个节点包含关键值、指向左右子树的指针以及一个表示该节点高度或平衡因子的值。
- 平衡因子:节点左右子树的高度差。在AVL树中,每个节点的平衡因子只能是-1、0或1。
- 旋转:为了维护树的平衡,AVL树在插入或删除节点后可能会进行旋转。旋转分为四种类型:右旋、左旋、左右旋和右左旋。
AVL节点的构建
我们构建一个K/V结构的AVL树,用来存储键值对。
由于牵扯到树形的连接,因此我们采用三叉链的形式,内部存储三个指针
同时_bf(binary factor)平衡因子用来检测树书否符规范
template<typename K, typename V>
struct AVLTreeNode {
//三叉连锁树的节点结构
AVLTreeNode* _left; //C++将struct之后的部分升级成了类名,可以直接用来创建对象
AVLTreeNode* _right;
AVLTreeNode* _parent;
pair<K, V> _kv;
int _bf; //平衡因子,左子树的高度减去右子树的高度 balance factor
AVLTreeNode(const pair<K, V>& kv)
: _left(nullptr),
_right(nullptr),
_parent(nullptr),
_kv(kv), //调用默认构造函数,初始化_kv
_bf(0)
{}
};
AVL树体的构建
成员变量只需要一个树根
Node* _root;
AVL树体重点是插入的实现
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 (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
//cur为空,说明找到了插入位置
cur = new Node(kv);
if (kv.first < parent->_kv.first)
{
parent->_left = cur; //连接
cur->_parent = parent;
}
else
{
parent->_right = 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->_right->_bf == -2) //调整(插入之前不可能为2或者-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);
// 1、旋转让这颗子树平衡了
// 2、旋转降低了这颗子树的高度,恢复到跟插入前一样的高度,所以对上一层没有影响,不用继续更新
break;
}
else
assert(false);
}
}
具体片段解析
1.空树直接插入
if (_root == nullptr) {
_root = new Node(kv);
return true;
}
2.寻找合适的位置插入(找到空位置)
while (cur)
{
if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
3.插入后进行连接
if (kv.first < parent->_kv.first)
{
parent->_left = cur; //连接
cur->_parent = parent;
}
else
{
parent->_right = cur;
cur->_parent = parent;
}
4.进行平衡因子的调整(重难点)
4.1根据插入的位置,插入左树,_bf--,否则++
if (cur == parent->_left)
parent->_bf--;
else
parent->_bf++;
4.2平衡因子发生变化之后,进行调整
若插入之后,abs(_bf) == 1,那说明该树处于临界状态,此时需要向上检查
若abs(_bf) == 2,说明插入之后,树已经失衡,需要进行旋转调整
同时,旋转之后,不仅完成了树的平衡,还使得高度--,树变成了平衡状态
此时abs(parent->_bf) == 2,abs(cur->_bf) == 1,所以四种情况
if (parent->_bf == 0)
break; //平衡
else if (parent->_bf == 1 || parent->_bf == -1) //需要向上检查
{
cur = parent;
parent = parent->_parent;
}
else if (parent->_bf == 2 || parent->_right->_bf == -2) //调整(插入之前不可能为2或者-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);
// 1、旋转让这颗子树平衡了
// 2、旋转降低了这颗子树的高度,恢复到跟插入前一样的高度,所以对上一层没有影响,不用继续更新
break;
}
else
assert(false);
旋转算法
void RotateR(Node* parent)
{
Node* SubL = parent->_left; //最关键的两个节点
Node* SubLR = SubL->_right;
//连接
parent->_left = SubLR;
SubL->_right = parent;
//处理剩余关系线
parent->_parent = SubL;
if (SubLR) //如果subLR为空,那么parent->right一定为空
SubLR->_parent = parent;
Node* parentParent = parent->_parent;
if (_root == parent) //有可能parent是根节点,也有可能不是根节点
{
_root = SubL;
subL->_parent = nullptr;
}
else //不是根节点
{
if (parent == parentParent->_left) //判断是左子树还是右子树
{
parentParent->_left = SubL;
subL->_parent = parentParent;
}
else
{
parentParent->_right = SubL;
subL->_parent = parentParent;
}
}
subL->_bf = 0;
parent->_bf = 0; //如果subLR为空,那么parent->right一定为空,最终的_bf一定都是空
}
void RotateL(Node* parent)
{
Node* SubR = parent->_right;
Node* SubRL = SubR->_left;
//连接
parent->_right = SubRL;
SubR->_left = parent;
//处理剩余关系线
parent->_parent = SubR;
if (SubRL) //如果subRL为空,那么parent->left一定为空
SubRL->_parent = parent;
Node* parentParent = parent->_parent;
if (_root == parent) //有可能parent是根节点,也有可能不是根节点
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parent == parentParent->_left) //判断是左子树还是右子树
{
parentParent->_left = SubR;
subR->_parent = parentParent;
}
else
{
parentParent->_right = SubR;
subR->_parent = parentParent;
}
}
subR->_bf = 0;
parent->_bf = 0; //如果subRL为空,那么parent->left一定为空,最终的_bf一定都是空
}
void RotateLR(Node* parent)
{
Node* SubL = parent->_left;
Node* SubLR = SubL->_right; //可能为空
int bf = SubLR->_bf; //可能为0(如果为0,说明SubLR为空)
RotateL(SubL);
RotateR(parent);
if (bf == 0)
{
//说明SubLR就是新增
parent->_bf = subL->_bf = subLR->_bf = 0;
}
else if (bf == 1)
{
//说明SubLR是新增的右子树
parent->_bf = 0;
subL->_bf = -1;
subLR->_bf = 0;
}
else if (bf == -1)
{
//说明SubLR是新增的左子树
parent->_bf = 1;
subL->_bf = 0;
subLR->_bf = 0;
}
else
{
assert(false);
}
}
void RotateRL(Node* parent)
{
Node* SubR = parent->_right;
Node* SubRL = SubR->_left; //可能为空
int bf = SubRL->_bf; //可能为0(如果为0,说明SubRL为空)
RotateR(SubR);
RotateL(parent);
if (bf == 0)
{
//说明SubRL就是新增
parent->_bf = subR->_bf = subRL->_bf = 0;
}
else if (bf == 1)
{
//说明SubRL是新增的右子树
parent->_bf = 0;
subR->_bf = -1;
subRL->_bf = 0;
}
else if (bf == -1)
{
//说明SubRL是新增的左子树
parent->_bf = 1;
subR->_bf = 0;
subRL->_bf = 0;
}
else
{
assert(false);
}
}
AVL树的验证
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int leftHeight = _Height(root->_left); //求的是子树的高度,不包括自身,不能加1
int rightHeight = _Height(root->_right);
return max(leftHeight, rightHeight) + 1;
}
bool _IsBalanced(Node* node) //进行遍历
{
if (node == nullptr)
return true;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
if (rightHeight - leftHeight != root->_bf)
{
cout << root->_kv.first << "平衡因子异常" << endl;
return false;
}
return abs(rightHeight - leftHeight) < 2
&& _IsBalance(root->_left)
&& _IsBalance(root->_right);
}