🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
文章目录
一、搜索二叉树缺点
如果是有序的或者接近有序,那么查找的效率就退化的很厉害,效率无法保证,那么就要开始尝试控制平衡
二、AVL概述
AVL数是以人名来命名的(G.M.Adelson-Velsk和E.<.Landis),发现一种解决搜索二叉树缺点的办法。当向二叉树中插入新节点时,如果能保证每个结点的左右子树高度之差绝对值不超过1,就能减少平均搜索长度
AVL树规则:
一棵AVL树 或者是空树,或者是具有以下性质的任意一棵树
①它的左右子树都是AVL树,也就是任意一颗子树左右高度差的绝对值都不超过1
②左右子树高度差绝对值不超过1,AVL这里用平衡因子来实现,平衡因子只能为-1,0,1、(实现AVL树也可以不用平衡因子)
AVL树也叫做高度平衡搜索二叉树
三、AVL树
①AVLTree的结构
struct AVLTree
{
AVLTreeNode<K,V>*left;
AVLTreeNode<K,V>*right;
AVLTreeNode<K,V>*parent;
pair<K,V> _kv;
int _bf;
//平衡因子 balance factor
};
并不是说AVL树一定有平衡因子,AVL树只规定了左右高度差不超过1,这里只是选择平衡因子这种方式来实现。平衡因子 = 右子树高度-左子树高度(新插入的结点平衡因子都是0)
②更新平衡因子的规则
平衡因子等于右树-左数高度
1.新增在右,parent->_bf++,新增在左,parent->_bf–;(1就代表右边高,-1就代表左边高)
2.更新后,parent->_bf==1or-1,说明parent插入前的平衡因子是0,说明parent的平衡因子之前就是0,说明之前左右子树高度相等,现在进行了插入,要往上进行平衡因子的调整
3.更新后,parent->_bf==0,这就说明这一次的插入只是平衡了上一次的高度差,parent所在的子树高度不变,所以不用继续往上更新
4.更新后,parent->_bf==2or-2,说明现在以及打破平衡因子临界值,parent所在的子树需要做旋转处理
5.更新后,parent->_bf>2或者<-2这是不可能的,因为到2或者-2的时候就已经旋转调整了,不可能出现这种情况,如果有,说明之前就错了。需要检查之前的过程
旋转的价值和意义:
1.树或者子树平衡
2.降高度
③AVL旋转
AVL树包括红黑树最复杂的地方就是在于旋转,所以重点掌握旋转和插入就可以了
新结点插入较高左子树的左侧:右单旋
因为现在要让40作为跟,所以把30的右挂在60的左,因为30的右一定小于60.然后再让30成为根即可
也就是说某个根的左子树高度>右子树高度 超过1,那么我进行右旋,按照上面的这种方式,右单旋需要保证左边树高并且最高的是最左边那个树(下面会说原因)
//旋转的并不是插入结点的parent,而是平衡因子不对劲的哪个结点
void RotateR(Node*parent)
{
Node*subL = parent->_left;
Node*subLR = subL->_right;
Node*ppNode = parent->_parent;
//旋转
parent->_left = subLR;
if(subLR)
subLR->_parent = parent;
subL->right = parent;
parent->_parent = subL;
//处理特殊
if(_root==parent)
{
_root=subL;
subL->_parent = nullptr;
}
else
{
if(ppNode->_left == parent)
{
ppNode->_left = subL;
}
else
{
ppNode->_right = subL;
}
subL->_parent = ppNode;
}
paret->_bf = subL->_bf =0;//旋转完后是平衡的,看图
}
右单旋后,parent和subL的平衡因子是会为0的,这个是从上图看出来的,上图包含了右单旋的全部情况
新结点插入较高右子树的右侧:左单旋
和右单旋同理,需要右树高并且右树的右树更高,直接写代码
void RotateL(Node*parent)
{
Node*subR = parent->_right;
Node*subRL = subR->_left;
parent->_right = subRL;
if(subRL)
subRL->_parent = parent;
Node*ppNode = parent->parent;
subR->_left = parent;
parent->_parent = subR;
if(_root == parent)
{
_root = subR;
_subR->_parent = nullptr;
}
else
{
if(ppNode->_left == parent)
{
ppNode ->_left = subR;
}
else
{
ppNode->_right = subR;
}
subR->_parent = ppNode;
}
subR->_bf = parent -> bf = 0;
//旋转后平衡,可验证
}
新结点插入较高左子树的右侧:左右双旋
这种情况如果只进行右单旋,会导致右子树又比左子树高,可以自己验证,所以我们需要把最高的首先进行旋转
void RotateLR(Node*parent)
{
Node*subL = parent ->_left;
Node*subLR = subL->_right;
int bf = subLR->_bf;//
RotateL(subL);
RotateR(parent);//复用搞定
//还需要我们去调整平衡因子,虽然RotateL或者RotateR代码中有调节平衡因子的
//但是别忘了有一次左旋是我们强制的,所以调节的平衡因子是无效的,我们需要自己手动调
//看图填空的感觉,因为包含了所有情况(上面的图还有其他的,比如高的在右边)
subLR->_bf==0;//看图得出,一定是0
if(bf==1)//说明是subLR右边插入
{
parent->bf=0;
subL->_bf =-1;
}
else if(bf==-1)
{
parent->_bf=1;
subL->_bf = 0;
}
else if(bf==0)
{
parent->_bf =0;
subL->_bf =0;
}
else
{
assert(false);//其他情况直接断言错误
}
}
新节点插入较高右子树的左侧:右左双旋
void RotateLR(Node*parent)
{
Node*subR = parent ->_right;
Node*subRL = subR->_left;
int bf = subRL->_bf;//
RotateL(subR);
RotateR(parent);//复用搞定
subLR->_bf==0;//看图得出,一定是0
if(bf==-1)//说明是subLR右边插入
{
parent->bf=0;
subL->_bf =1;
}
else if(bf==1)
{
parent->_bf=-1;
subL->_bf = 0;
}
else if(bf==0)
{
parent->_bf =0;
subL->_bf =0;
}
else
{
assert(false);//其他情况直接断言错误
}
}
④判断是否为AVL树
AVL树又叫做高度平衡搜索二叉树,所以需要检查是否每棵树都高度平衡,最好再进行中序遍历看看是否 去重并排序
public:
bool IsBalance()
{
return _IsBalance(_root);
}
private:
bool _IsBalance(Node*root)
{
if(root==nullptr) return true;
int leftHT = Height(root->left);//求高度
int rightHT = Height(root->right);
return abs(rightHT-leftHT)<2
&&_IsBalance(root->_left)
&&_IsBalance(root->_right);
}