树的构建——
1.节点构建:
了解了AVL树的构建后,首先可以得出它每个节点必须同二叉树一样保存左右节点指针,还有平衡因子。我们后续需要进行旋转,则必须还需要一个指向父亲的指针(根节点为nullptr)。这样就造就了AVL树必须采用三叉链的结构。
template<class K,class V>
struct AVLTreeNode
{
AVLTreeNode<K,V>* _left;
AVLTreeNode<K,V>* _right;
AVLTreeNode<K,V>* _parent;
pair<K,V> _kv; //存储的为pair,以构建map与set为例
int _bf; //平衡因子
AVLTreeNode(const pair<K,V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_bf(0)
{}
};
这里也不一定要用Key,Value形式,也可以用template<class T>替换模板,下面依次修改就好。
树的插入——
树的插入同搜索二叉树一样,与根相比,比根节点大往右边走,比根节点小往左边走,再下来与路径上节点比较,与根比较同理。走到nullptr为止。这时我们就可以开始插入。但插入过后并不是置之不理,不然就同搜索树一样了,我们得保持树的高度平衡——即看旋转因子的大小。
我们来看看节点的插入的规则——
template<class K,class V>
struct AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
void 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->_right;
}
else
return false;
}
//挂节点
cur = new Node(kv);
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
//保持树的平衡,对树进行旋转——
//...
private:
Node* _root = nullptr;
};
树的旋转——
控制平衡,并更新平衡因子
更新规则:
新增在左,则parent->_bf--;新增在右,则parent->_bf++
1,新增在右,parent->bf++;新增在左,parent->bf-
2.更新后,parent->bf == 1 or -1, 说明parent插入前的平街因子是0,
说明左右子树高度相等,插入后有一边高,parent高度变了,需要继续往上更新
3、更新后,parent->bf == 0,说明parent插入前的平衡因子是1 or -1,
说明左右子树一边高一边低,插入后两边一样高,插入填上了矮了那边。parent
所在子树高度不变,不需要樂续往上更新
4 更新后,parent->bf == 2 or -2,说明parent插入前的平衡因子是1or - 1,己经平衡临值,
插入变成2or - 2,打破平衡parent所在子树需要旋转处理。
5.更新后,parent->bf>2or< - 2的值,不可能,如果存在,
则说明插入前就不是AVL树,需要去检查之前操作的问题。
于是我们可以用一个while循环来进行更新——
//...(将以下代码放置在上述代码残缺位置)
while (parent)
{
if (cur == parent->_right)
{
parent->_bf++;
}
else
{
parent->_bf--;
}
if (parent->_bf == 0)
{
break;
}
else if (abs(parent->_bf) == 1)
{
parent = parent->_parent;
cur = cur->_parent;
}
else if (abs(parent->_bf) == 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)
{
RotateLR(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1)
{
RotateRL(parent);
}
}
else //出现在这里说明之前的平衡因子就已经不平衡
assert(false);
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR) //注意判断是否为空
{
subLR->_parent = subL;
}
Node* pparent = parent->_parent; //根不一定为parent,则必须记录parent的父亲节点,以便在旋转后链接
subL->_right = parent;
parent->_parent = subL;
if (_root == parent)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (pparent->_left == parent)
{
pparent->_left = subL;
}
else
{
pparent->_right = subL;
}
subL->_parent = pparent;
}
subL->_bf = parent->_bf = 0;
}
情况和右单旋类似,可参照右单旋方式——
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
Node* pparent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (parent == _root)
{
parent = subR;
subR->_parent = nullptr;
}
else
{
if (parent = pparent->_left)
{
pparent->_left = subR;
}
else
{
pparent->_right = subR;
}
subR->_parent = pparent;
}
subR->_bf = parent->_bf = 0;
}
但是,当我们插入节点不是最左或者最右节点时,我们仅仅依靠单旋并不能解决所有问题——
比如以上情况,仅仅依靠单旋,只能使其陷入一个死循环,而高度平衡的问题并未得到有效解决。这时候我们就可以考虑使用两次旋转,分别以30为轴点进行左单旋,再以60为轴点进行右单旋,如下如所示——
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(parent->_left); //复用之前代码
RotateR(parent);
subLR->_bf = 0;
if (bf == 1) //新增在右子树
{
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 RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
subRL->_bf = 0;
if (bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
subR->_bf = 1;
parent->_bf = 0;
}
else if (bf == 0)
{
parent->_bf = 0;
subR->_bf = 0;
}
else
{
assert(false);
}
}
关于AVL树的性能——