目录
新节点插入较高右子树的左侧---右左:先右单旋再左单旋编辑
原理
AVL 树是一种平衡搜索二叉树,得名于其发明者的名字( Adelson-Velskii 以及 Landis)。(可见名字长的好处,命名都能多占一个字母出来)。在搜索树的前提下平衡搜索二叉树还定义如下:
- 左右子树的高度差小于等于 1。
- 其每一个子树均为平衡二叉树。
我们以SGI版本AVL树
先来查看树中每个结点内容。
template<class K,class V>
struct AVLTreeNode
{
AVLTreeNode* _left;//左子树
AVLTreeNode* _right;//右子树
AVLTreeNode* _parent;//父节点
pair<K, V> _kv;//数据存储内容
int _bf;//平衡因子
AVLTreeNode(pair<K, V>kv=pair<K, V>())//默认赋值构造
:_kv(kv)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
,_bf(0)
{}
};
左子树与右子树指针我们不做过多介绍。
我们存储AVL树是一种存储key_value数据的树,所以我们使用pair库中类型存储数据。
让我们看看平衡因子是什么:
平衡因子
某个结点的右子树的高度减去左子树的高度得到的差值。
平衡因子在[-1,1]之间都是属于平衡的平衡搜索二叉树,每个结点都需要符合这个要求
在插入的过程中我们会破坏平衡,这个时候就需要对树结点进行转至操作。
AVL树的插入insert
和普通搜索二叉树一样,先要寻找插入的位置。
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
}
else
{
Node* parent = nullptr;//保存上级指针,我们链接都是cur走到空指针创建结点给cur
//但是这个新的结点需要被链接到树中,我们不可以对
//nullptr进行访问上级的数据,所以我们必须留一个parent
//用来链接新的结点。
Node* cur = _root;
while (cur)
{
parent = cur;
if (cur->_kv.first > kv.first)
{
cur = cur->_left;//新数据小于当前数据,向cur的左边走
}
else if (cur->_kv.first < kv.first)
{
cur = cur->_right;//新数据大于当前数据,向cur的右边走
}
else
{
return false;//数据相同退出,并返回false
}
}
cur = new Node(kv);//cur到了nullptr,创建新的结点。
//新结点链接到树
if (parent->_kv.first <cur->_kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;//处理新结点的_parent
插入结点后我们需要修改平衡因子,确保插入数据后我们的树依旧是AVL树。
更新平衡因子规则:
- 新增在右,parent->bf+ +;新增在左,parent->bf--;
- 更新后,parent->bf == 1 or -1,说明parent插入前的平衡因子是0,说明左右子树高度相等,插入后有一边高,parent高度变了,该节点插入稍稍改变平衡,需要继续往上更新平衡因子。
- 重新后,parent->bf == 0说parent插入前的平衡因子是1 or -1,说明左右子树一边高一边低,插入后两边一样高,插入填上了矮了那边,parent所在子树高度不变,不需要继续往上更新
- 更新后,parent->bf == 2 or -2,说明parent插入前的平衡因子是1 or -1,已经平衡临界值,插入变成2 or -2,打破平衡,parent所在子树需要旋转处理
- 更新后,parent->bf > 2 r< -2的值,不可能,如果存在,则说明插入前就不是AVL树,需要去检查之前操作的问题,不需要再去考虑现在代码的问题了。
while (parent)
{
if (parent->_right == cur)//根结点(praent)的右子树(cur)插入了一个结点当前根结点平衡因子++
{
++parent->_bf;
}
else /*if() 可以不写这个*///根结点(praent)的左子树(cur)插入了一个结点当前根结点平衡因子--
{
--parent->_bf;
}
if (parent->_bf == 0)//修改更新平衡因子后,查看当前平衡因子,为0代表根补齐了该根的低子树
//parent所在子树高度不变,不需要继续往上更新