AVL树特点
正常的搜索二叉树中,一般而言查找一个数的时间复杂度几乎可以等价于log2N,但是正常的搜索二叉树可能会遇到一些极端情况,例如按照1,2,3,4插入数组,此时的搜索二叉树便会变为如下的情况:
这种数据有序或者接近有序的插入数据此时的搜索二叉树会退化为一个单分支,此时查找效率就变成了O(n),并不能体现搜索二叉树的优势。此时,有两位数学家提出了一种方法来解决这种问题:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。这种让搜索二叉树的高度平衡,此时他有n个节点,他的高度可以保持着log2n,因此可以达到log2n的时间复杂度,极大的提高了效率。
AVL树的实现
AVL树节点的定义
AVL树有多种控制高度的方式,这里介绍的是采用平衡因子的方式,即在每个节点保存一个平衡因子,它用来存放该节点的左右子树的高度差。后续通过判断平衡因子大小就可以判断该节点是否平衡。代码定义如下:
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;//balance factor
AVLTreeNode(const pair<K, V>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _bf(0)
, _kv(kv)
{
}
};
AVL树节点的插入
AVL树是由搜索二叉树发展而来的,因此他的插入方式和搜索二叉树类似,但是为了保持高度平衡,需要在插入节点后,判断此时是否平衡,如果不平衡则需要进行调整。插入代码如下:
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;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
return true;
}
到这里为止,和普通搜索二叉树的插入方式基本一致,只需要多处理这里的三叉链的父节点的链接即可。下面需要注意的就是如果此时的二叉树高度不平衡则需要进行高度调整。
高度调整需要先调整二叉树的平衡因子,代码如下:
调整原则为如果当前节点为父节点的左节点则进行平衡因子减一操作,如果为右节点则进行加1操作,以此向上更新,如果此时父节点的平衡因子变为0,就不需要再向上调整了,因为该部分已经平衡了。如果父节点平衡因子变为2或者-2则此时二叉树已经不平衡了,需要对其进行高度调整。
while (parent)
{
if (cur == parent->_left)
{
parent->_bf--;
}
else
{
parent->_bf++;
}
if (parent->_bf == 0)
{
break;
}
else if (parent->_bf == 1 || parent->_bf == -1)
{
//继续往上更新
cur = parent;
parent = parent->_parent;
}
else if (parent->_bf == 2 || parent->_bf == -2)
{
//旋转处理
//右单旋
if (parent->_bf == -2 && cur->_bf == -1)
{
RotateR(parent);
}
//左单旋
else if (parent->_bf == 2 && cur->_bf == 1)
{
RotateL(parent);
}
//左右双旋
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1)
{