AVL树的概念及特点
AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis,AVL树是最先发明的自平衡二叉查找树。
二叉搜索树(二叉查找树,二叉排序树)或者是一棵空树,或者是具有下列性质的二叉树:
- 若左子树不空,则左子树上所有节点的值均小于它的根节点的值;
- 若右子树不空,则右子树上所有节点的值均大于它的根节点的值;
- 左、右子树也分别为二叉搜索树;
- 没有键值相等的节点。
AVL树是二叉搜索树的一种特殊情况,本质还是一颗二叉搜索树,只是AVL树的每个节点的两个子树的高度之差的绝对值(平衡因子)最多为1,也就是说,AVL树本质上是带了平衡功能的二叉搜索树,也被称为高度平衡树。如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在O(logN) ,搜索的时间复杂度为O(logN) 。
AVL树节点的定义
template <class T>
struct AVLNode
{
AVLNode(const T& data)
: _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _bf(0)
{}
AVLNode<T>* _left; // 该节点的左孩子
AVLNode<T>* _right; // 该节点的右孩子
AVLNode<T>* _parent; // 该节点的双亲
T _data;
int _bf; // 该节点的平衡因子
};
AVL树的插入
AVL树的插入过程可以分为两步:
- 按照二叉搜索树的方式插入新节点;
- 调整节点的平衡因子。
AVL树的旋转
如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡。
根据节点插入位置的不同,AVL树的旋转分为四种:
- 新节点插入较高左子树的左侧—左左:右单旋
- 新节点插入较高右子树的右侧—右右:左单旋
- 新节点插入较高左子树的右侧—左右:先左单旋再右单旋
- 新节点插入较高右子树的左侧—右左:先右单旋再左单旋
总结:
假如以Parent为根的子树不平衡,即Parent的平衡因子为2或者-2,分以下情况考虑:
- Parent的平衡因子为2,说明Parent的右子树高,设Parent的右子树的根为SubR
当SubR的平衡因子为1时,执行左单旋
当SubR的平衡因子为-1时,执行右左双旋 - Parent的平衡因子为-2,说明Parent的左子树高,设Parent的左子树的根为SubL
当SubL的平衡因子为-1时,执行右单旋
当SubL的平衡因子为1时,执行左右双旋
旋转完成后,原以Parent为根的子树高度降低,已经平衡,不需要再向上更新。
AVL树的代码实现
- AVL树的结构
template <class T>
class AVLTree
{
public:
typedef AVLNode<T> Node;
typedef Node* pNode;
bool Insert(const T& date);//插入
void RotateRight(pNode cur); //右旋
void RotateLeft(pNode cur);//左旋
void _Inorder(pNode root);
void Inorder();//中序遍历打印
int Height(pNode root);
bool _isBalance(pNode root);
bool isBalance();//判断是否平衡
private:
pNode _root = nullptr;
};
- 插入
template<class T>
bool AVLTree<T>::Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
return true;
}
//按照二叉搜索树的性质找data在AVL中的插入位置
pNode cur = _root;
pNode parent = nullptr;
while (cur)
{
parent = cur;
if (data > cur->_data)
cur = cur->_right;
else if (data < cur->_data)
cur = cur->_left;
else
return false;
}
//插入新节点
cur = new Node(data);
if (data > parent->_data)
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)
// 双亲的平衡因子为正负2,违反了AVL树的平衡性,需要对以parent为根的树进行旋转处理
{
//左边的左边高
if (parent->_bf == -2 && cur->_bf == -1)
{
RotateRight(parent);
}
//右边的右边高
else if (parent->_bf == 2 && cur->_bf == 1)
{
RotateLeft(parent);
}
//右边的左边高
else if (parent->_bf == 2 && cur->_bf == -1)
{
pNode subR = parent->_right;
pNode subRL = subR->_left;
RotateRight(subR);
RotateLeft(parent);
if (subRL->_bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
}
else if (subRL->_bf == -1)
{
subR->_bf = 1;
parent->_bf = 0;
}
}
//左边的右边高(左右旋)
else if (parent->_bf == -2 && cur->_bf == 1)
{
pNode subL = parent->_left;
pNode subLR = subL->_right;
RotateLeft(subL);
RotateRight(parent);
if (subLR->_bf == 1)
{
parent->_bf = 0;
subL->_bf = -1;
}
else if (subL->_bf == -1)
{
parent->_bf = 1;
subL->_bf = 0;
}
}
break;
}
}
return true;
}
- 左旋
template<class T>
void AVLTree<T>::RotateLeft(pNode cur)
{
pNode subR = cur->_right;
pNode subRL = subR->_left;
subR->_left = cur;
cur->_right = subRL;
if (subRL)
subRL->_parent = cur;
if (cur != _root)
{
if (cur->_parent->_left == cur)
cur->_parent->_left = subR;
else
cur->_parent->_right = subR;
subR->_parent = cur->_parent;
}
else
{
_root = subR;
subR->_parent = nullptr;
}
cur->_parent = subR;
subR->_bf = cur->_bf = 0;
}
- 右旋
template<class T>
void AVLTree<T>::RotateRight(pNode cur)
{
pNode subL = cur->_left;
pNode subLR = subL->_right;
subL->_right = cur;
cur->_left = subLR;
if (subLR)
subLR->_parent = cur;
if (cur != _root)
{
if (cur->_parent->_left == cur)
{
cur->_parent->_left = subL;
}
else
{
cur->_parent->_right = subL;
}
subL->_parent = cur->_parent;
}
else
{
_root = subL;
subL->_parent = nullptr;
}
cur->_parent = subL;
subL->_bf = cur->_bf = 0;
}
- 中序遍历打印
template<class T>
void AVLTree<T>::_Inorder(pNode root)
{
if (root)
{
_Inorder(root->_left);
cout << root->_data << " ";
_Inorder(root->_right);
}
}
template<class T>
void AVLTree<T>::Inorder()
{
_Inorder(_root);
cout << endl;
}
- 检查是否平衡
template<class T>
int AVLTree<T>::Height(pNode root)
{
if (root == nullptr)
return 0;
int Lh = Height(root->_left);
int Rh = Height(root->_right);
return Lh > Rh ? Lh + 1 : Rh + 1;
}
template<class T>
bool AVLTree<T>::_isBalance(pNode root)
{
if (root == nullptr)
return true;
int Rh = Height(root->_right);
int Lh = Height(root->_left);
if (root->_bf != Rh - Lh)
{
cout << root->_data << "不平衡" << endl;
cout << "平衡因子:" << root->_bf << "高度差:" << Rh - Lh << endl;
return false;
}
return abs(root->_bf) < 2 && _isBalance(root->_left) && _isBalance(root->_right);
}
template<class T>
bool AVLTree<T>::isBalance()
{
return _isBalance(_root);
}
AVL树的性能
- AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即O(logN) 。
- 如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。
- 如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。