AVL树节点中存有高度差作为平衡因子,高度差为右树高度-左数高度。
1、按照搜索树规则插入
2、插入节点后只会影响到该节点的祖先的平衡性,因此更新插入节点的祖先节点的平衡因子。
- 插入到父亲的左边,父亲的平衡因子- -。 插入到父亲的右边,父亲的平衡因子++。
- 如果父亲的平衡因子==0,父亲所在的子树高度不变,则不需要在更新祖先的平衡因子,插入结束。
- 如果插入以后,父亲的平衡因子是1或者-1,父亲所在的子树高度变了,继续往上更新。转到1处继续更新(就是说节点是1或者-1,就得继续向上找父亲,然后如果我是父亲的左树,父亲- -,我是父亲的右树,父亲++。)。
- 父亲的平衡因子为2或者-2,则父亲所在的子树已经不平衡了,需要旋转。
AVLTree的节点:需要有平衡因子,左节点,右节点,父节点的信息,AVL中存的是pair类型的kv键值对。
AVLTree的插入:插入就按照二叉搜索树插入节点,但是插入完成后需要把当前节点的父节点给指明,avl树的节点是三个链,左右父。
节点先插入后:
- 需要判断插入节点的方向,并对父节点及其祖先的平衡因子进行计算。
- 更新双亲的平衡因子,
- 判断平衡因子的值,如果插入前父亲的平衡因子是0,插入后父亲的平衡因子为1 或者 -1,说明以父亲为根的二叉树的高度增加了一层,因此需要继续向上调整。如果平衡因子的值为2或者-2,说明需要旋转调整。如果插入后父亲的平衡因子为0就终止。
AVL树的旋转:
1:新插入的子树在左子树的左侧,左左 - - 右单旋
上图在插入前,AVL树是平衡的,新节点插入到30的左子树(注意:此处不是左孩子)中,30左子树增加了一层,导致以60为根的二叉树不平衡,要让60平衡,只能将60左子树的高度减少一层,右子树增加一层,即将左子树往上提,这样60转下来,因为60比30大,只能将其放在30的右子树,而如果30有右子树,右子树根的值一定大于30,小于60,只能将其放在60的左子树,旋转完成后,更新节点的平衡因子即可。在旋转过程中,有以下几种情况需要考虑:
- 30节点的右孩子可能存在,也可能不存在
- 60可能是根节点,也可能是子树如果是根节点,旋转完成后,要更新根节点如果是子树,可能是某个节点的左子树,也可能是右子树
当插入的节点是在左边,父节点也是左子树,影响了平衡,就是 左左- - - 右单旋的思路:
传入bf=-2的节点为parent,让subl=parent->_left,sublr=subl->_right。
1.如果sublr不等于Nullptr,则让sublr->_parent=parent。
2.subl->_right=parent,parent->_parent=subl。
完成1,2旋转就结束了,但是需要注意:
parent有可能是root也可能不是root,如果是root,直接让root = subl。
如果不是root,让 ppnode = parent->_parent:
需要先判断 parent 是它父亲的左子树还是右子树,
如果是左子树:让ppnode->_left = subl,如果是右子树:ppnode->_right = subl。
最后让subl->_parent = ppnode。
最后让parent->_bf=subl->_bf=0。
具体分析如下图:
如果是插入的节点在右边,父节点也是右子树,影响了平衡,就是右右 - - - 左单旋。
思路和右单旋相同,如图:
如果插入的节点在比较深的子树的右侧,构成一个折线型的结构,60=-1,30=1,90=-2,就是一个右左右的不平衡,需要先左在右旋转 。
其中,新插入的节点如果在b的下边,刚开始60->bf = = -1,则旋转完成后 需要设置parent->bf = = 1。
但也有可能插入在c的下边,如果在c的下边,则刚开始 60->bf == 1,旋转完成后需要设置 subl->bf = = -1。
对30左单旋后,subl、sublr的 bf 设置为0,对90右单旋后,会把sublr、parent 的 bf 设置为0。因此,在旋转之前需要先拿到 sublr 对应的 bf ,旋转完成后才可以正确更新parent 或者 subl 的bf。
下图所示的为新增节点在 b 的下边,但实际代码需要考虑在c下边的情况。
如果新插入的节点在较深右子树的左侧:**是一个左右左结构,先右单旋再左单旋。**理解上和先左再右一样,分析如下图:
验证是否为平衡树,需要计算当前节点的左子树高度和右子树高度,然后右高度-左高度得到的值判断和bf是否相等,不相等就不平衡。
下图为求树节点个数和树最大深度的普遍写法:
完整的AVL旋转代码如下:
cpp
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)
{}
};
template<class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
AVLTree()
:_root(nullptr)
{}
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)
{
// 插入前双亲的平衡因子是0,插入后双亲的平衡因为为1 或者 -1 ,
//说明以双亲为根的二叉树 的高度增加了一层,因此需要继续向上调整
cur = parent;
parent = parent->_parent;
}
else if (parent->_bf == 2 || parent->_bf == -2)
{
//当前子树不平衡了,因此需要旋转。
//如果是当前节点为-1,parent=-2 相当于连续在左 左 的下面插入了一个,则需要右单旋
if (cur->_bf == -1 && parent->_bf == -2)
{
RotateR(parent);
}
//如果是当前节点为1,parent=2 相当于连续在右 右 的下面插入了一个,则需要左单旋
else if (cur->_bf == 1 && parent->_bf == 2)
{
RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1)
{
RotateRL(parent);
}
break;
}
else //理论上不可能走到这一度,但是得写防止自己写的代码出问题
{
assert(false);
}
}
return true;
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
subL->_right = parent;
Node* ppnode = parent->_parent; //得先把ppnode存下来才能更改parent->_parent
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = subL;
}
else
{
ppnode->_right = subL;
}
subL->_parent = ppnode;
}
parent->_bf = subL->_bf = 0;
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = subR;
subR->_left = parent;
Node* ppnode = parent->_parent;
parent->_parent = subRL;
if (parent == _root)
{
_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);
if (bf == 1)
subL->_bf = -1;
else
parent->_bf = 1;
}
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(subR);
RotateL(parent);
if (bf == -1)
subR->_bf = 1;
if (bf == 1)
parent->_bf = -1;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
Node* Find(const K& key) // k v 类型,通过k找到v并可以得到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;
}
bool IsBalance() //判断是否平衡
{
return _IsBalance(_root);
}
int Height() //判断当前节点的深度
{
return _Height(_root);
}
int Size()
{
return _size(_root);
}
private:
int _Size(Node* root)
{
return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
return max(_Height(root->_left), _Height(root->_right)) + 1;
}
bool _IsBalance(Node* root)
{
if (root == nullptr)
return true;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
//平衡因子是否合理,不合理打印出不合理的那个点:
if (abs(leftHeight - rightHeight) >= 2)
{
cout << "平衡因子不合理: " << root->_kv.first << " bf = " << root->_bf << endl;
return false;
}
//检查平衡因子是否正确,不正确打印出不正确的那个点
if (rightHeight - leftHeight != root->_bf)
{
cout << "平衡因子不正确: " << root->_kv.first << " bf = " << root->_bf << endl;
return false;
}
return _IsBalance(root->_left)
&& _IsBalance(root->_right);
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
private:
Node* _root;
};