目录
一,AVL树
二,AVL树的旋转
LL(右单旋)
RR(左单旋)
LR(先左单旋再右单旋)
RL(先右单旋再左单旋)
三,AVL树的验证及删除
AVL树的验证
AVL树的删除(了解)
四,AVL树的性能
关联式容器set/multiset、map/multimap的底层都是按照二叉搜索树实现的,但二叉搜索树其本身是有缺陷的;如插入的元素是有序或近似有序的,二叉搜索树就会退化为单支树,实际复杂度为O(N);
一,AVL树
二叉搜索树,如数据是有序或近似有序的将退化为单支树,查找元素相当于在顺序表中搜索元素,效率较低;因此,俄罗斯数学家(G.M.Adelson-Velskii和E.M.Landis)在1962年发明了一种解决上述问题的方法,即当向二叉搜索树中插入新节点后,如能保证每个节点的左右子树高度之差的绝对值不超过1(需对树中节点进行调整),降低树的高度,从而减少平均搜索长度;
AVL树(平衡二叉树Self-balancing binary search tree)
- 或是一颗空树;
- 或是具有左右子树都是AVL树,且左右子树高度差(简称平衡因子)绝对值不超过1;
平衡因子BF(Balance Factor)
- 左子树高度减去右子树高度的值;平衡二叉树BF取值范围[-1,1],如某节点BF值超过范围就需调整;
//AVL树节点的定义
template
struct AVLTreeNode
{
AVLTreeNode(const T& data)
:_pLeft(nullptr)
,_pRight(nullptr)
,_pParent(nullptr)
,_data(data)
,_bf(0)
{}
AVLTreeNode* _pLeft; //当前节点的左孩子
AVLTreeNode* _pRight; //当前节点的右孩子
AVLTreeNode* _pParent; //当前节点的双亲
T _data;
int _bf; //平衡因子
}
二,AVL树的旋转
AVL树在二叉搜索树的基础上引入了平衡因子,也可看成是二叉搜索树;在一颗平衡的AVL树中插入一个新节点,造成不平衡,就需调整结构使之平衡;
AVL树的插入过程分为两步:
- 先按照二叉搜索树的方式插入新节点;
- 在调整节点的平衡因子;
根据节点插入的位置不同,AVL树的旋转可分为四种:LL(右单旋)、RR(左单旋)、LR(先左单旋再右单旋)、RL(先右单旋再左单旋);
LL(右单旋)
void _RotateR(pNode pParent)
{
pNode pSubL = pParent->_pLeft; //pParent左孩子
pNode pSubLR = pSubL->_pRight; //pParent左孩子的右孩子
pParent->_pLeft = pSubLR;
//若pSubLR存在
if (pSubLR)
pSubLR->_pParent = pParent;
pSubL->_pRight = pParent;
//若pParent可能是子树,存在双亲节点
pNode pPParent = pParent->_pParent;
pParent->_pParent = pSubL;
pSubL->_pParent = pPParent;
if (pPParent == NULL)
{
_pRoot = pSubL;
pSubL->_pParent = NULL;
}
else
{
//pParent可能是左子树,也可能是右子树
if (pPParent->_pLeft == pParent)
pPParent->_pLeft = pSubL;
else
pPParent->_pRight = pSubL;
}
//更新部分节点的平衡因子
pParent->_bf = pSubL->_bf = 0;
}
RR(左单旋)
void _RotateL(pNode pParent)
{
pNode pSubR = pParent->_pRight; //pParent右孩子
pNode pSubRL = pSubR->_pLeft; //pParent右孩子的左孩子
pParent->_pRight = pSubRL;
//若pSubRL存在
if (pSubRL)
pSubRL->_pParent = pParent;
pSubR->_pLeft = pParent;
//若pParent可能是子树,存在双亲节点
pNode pPParent = pParent->_pParent;
pParent->_pParent = pSubR;
pSubR->_pParent = pPParent;
if (pPParent == NULL)
{
_pRoot = pSubR;
pSubR->_pParent = NULL;
}
else
{
//pParent可能是左子树,也可能是右子树
if (pPParent->_pLeft == pParent)
pPParent->_pLeft = pSubR;
else
pPParent->_pRight = pSubR;
}
//更新部分节点的平衡因子
pParent->_bf = pSubL->_bf = 0;
}
LR(先左单旋再右单旋)
void _RotateLR(pNode pParent)
{
pNode pSubL = pParent->_pLeft;
pNode pSubLR = pSubL->_pRight;
int bf = pSubLR->_bf;
_RotateL(pParent->_pLeft);
_RotateR(pParent);
if (bf == 1)
pSubL->_bf = -1;
else if (bf == -1)
pParent->_bf = 1;
}
RL(先右单旋再左单旋)
void _RotateLR(pNode pParent)
{
pNode pSubR = pParent->_pRight;
pNode pSubRL = pSubR->_pLeft;
int bf = pSubRL->_bf;
_RotateR(pParent->_pRight);
_RotateL(pParent);
if (bf == -1)
pSubR->_bf = 1;
else if (bf == 1)
pParent->_bf = -1;
}
注:pParent为根的子树不平衡
旋转完成后,原pParent为根的子树高度减低,已经平衡,不需要再向上更新;
三,AVL树的验证及删除
AVL树的验证
AVL树是在二叉搜索树的基础上加入了平衡限制,要验证AVL树,可分为两步;
int _Height(pNode pRoot);
bool _IsBalanceTree(pNode pRoot)
{
if (pRoot == nullptr)
return true;
int leftHeight = _Height(pRoot->_pLeft);
int rightHeight = _Height(pRoot->_pRight);
int diff = rightHeight - leftHeight;
if (diff != pRoot->_bf || (diff > 1 || diff < -1))
return false;
return _IsBalanceTree(pRoot->_pLeft) && _IsBalanceTree(pRoot->_pRight);
}
AVL树的删除(了解)
因为AVL树也是二叉搜索树,可按照二叉搜索树的方式将节点进行删除。
然后在更新平衡因子,删除节点后的平衡因子更新,最差情况下一直要调整到根节点为止;
注:可参考《算法导论》或《数据结构-用面向对象方法与C++描述》;
四,AVL树的性能