目录
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树时,二叉搜索树的效率就会很低。因此出现了AVL树,AVL树是一种高度平衡的二叉搜索树,AVL树的每个结点的左右子树高度差的绝对值不超出1,是通过旋转的方式来维护这颗树的结构
结点的定义
template<class K, class V>
struct AVLTreeNode {
//三叉链
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
//存储的键值对
pair<K, V> _kv;
//平衡因子
int _bf; //右子树高度-左子树高度
//构造函数
AVLTreeNode(const pair<K, V>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv)
, _bf(0)
{}
};
这里引用了平衡因子的概念,平衡因子是通过右树减去左树来得到的,当一个树的平衡因子的绝对值大于等于2是,便会不平衡,这时就需要通过旋转的方式将树重新平衡起来。
左旋
当我们插入第三个结点时,树就会变得不平衡,我们就需要旋转处理。
且当parent的_bf等于2,childR的_bf是1时,我们左旋转
我们需要注意:这里的parent不一定根节点,也可能是一颗子树 ,我们需要定义一个指针,指向parent的_parent;
代码实现:
void RotateL(Node* parent)
{
Node* ChildR = parent->_right;
Node* ChildRL = ChildR->_left;
Node* parentParent = parent->_parent;
链接childRL和parent
parent->_right=childRL;
if(childRL)
childRL->_parent=parent;
链接childR和parent
childR->_left=parent;
parent->_parent=childR
//判断parent是否是根节点
if(_root==ParRar) {
_root=childR;
childR->_parent=nullptr;
}
else {
if(parpar->_left=parent)
parpar->_left=childR;
else if(parpar->_right=parent)
parpar->_right=childR;
childR->_parent=parpar;
}
//调整平衡因子
childR->_bf=parent->_bf=0;
}
右旋:
和左旋类似
且当parent的_bf等于-2,cildL的_bf是-1时,我们右旋转
我们需要注意:这里的parent不一定根节点,也可能是一颗子树 ,我们需要定义一个指针,指向parent的_parent;
代码实现:
void RotateR(Node* parent)
{
Node* ChildL = parent->_left;
Node* ChildLR = subL->_right;
Node* parpar = parent->_parent;
//链接parent和 ChildRL
parent->_right=ChildRL;
if(Child)
childRL->_parent=parent;
//链接·parent和childR
ChildR->_left=parent;
parent->_parent=ChildR;
if(_root==parent)
{
_root=ChildR;
ChildR->_parent=nullptr;
}
else
{
if(parpar->_left=parent)
parpatr->_left=ChildR;
else if(parpar->_right=parent)
parpar->_right=ChildR;
ChildR->_parent=parpar;
}
parent->_bf=ChildR->_bf=0;
}
右左双旋:
当parent的平衡因子为2,childR的平衡因子-1是,都构成右左双旋。
我们需要先以chidlR进行一次右旋,在_parent进行一次左旋
注意:在childRL的左右子树插入时,对平衡因子的处理不同,所以要分类讨论,我们定义一个变量bf来判断是在左子树或右子树,
当bf=-1是:
当bf=1时:
代码实现:
void RotateRL(Node* parent)
{
Node* childR = parent->_right;
Node* childRL = childR->_left;
int bf = childRL->_bf;
//1、以childR为轴进行右单旋
RotateR(childR);
//2、以parent为轴进行左单旋
RotateL(parent);
//3、更新平衡因子
if (bf == 1)
{
childRL->_bf = 0;
parent->_bf = -1;
childR->_bf = 0;
}
else if (bf == -1)
{
childRL->_bf = 0;
parent->_bf = 0;
childR->_bf = 1;
}
else if (bf == 0)
{
childRL->_bf = 0;
parent->_bf = 0;
childR->_bf = 0;
}
}
左右双旋:
当parent的平衡因子为-2,childL的平衡因子1是,就构成左右双旋。
我们需要先以chidlL进行一次左旋,在_parent进行一次右旋
注意:在childLR的左右子树插入时,对平衡因子的处理不同,所以要分类讨论,我们定义一个变量bf来判断是在左子树或右子树,
当bf=-1时:
当bf=1时:
代码实现:
void RotateLR(Node* parent)
{
Node* childL = parent->_left;
Node* childLR = subL->_right;
int bf = childLR->_bf; //subLR不可能为nullptr,因为subL的平衡因子是1
//1、以subL为旋转点进行左单旋
RotateL(childL);
//2、以parent为旋转点进行右单旋
RotateR(parent);
//3、更新平衡因子
if (bf == 1)
{
childLR->_bf = 0;
childL->_bf = -1;
parent->_bf = 0;
}
else if (bf == -1)
{
childLR->_bf = 0;
childL->_bf = 0;
parent->_bf = 1;
}
else if (bf == 0)
{
childLR->_bf = 0;
childL->_bf = 0;
parent->_bf = 0;
}
}
以上便是旋转的四种方式
章尚有不足,欢迎大牛指正
如果本文对您有帮助请点赞支持一下~