AVL树
平衡二叉搜索树
我们知道当二叉搜索树退化为单支树时,该树就变得不平衡了,所以为了解决此问题,就引出了平衡二叉搜索树-----当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1,这样的树称为平衡二叉搜索树。
平衡二叉搜索树有两种:其中一种为AVL树,另一种为红黑树。
AVL树
性质:
1.必须是二叉搜索树。
2.左右子树都是AVL树。
3.左右子树高度之差(称为平衡因子)的绝对值不超过1。
4.AVL树搜索时,时间复杂度为O(lgN)。
AVL树的操作:
AVL树结点的定义:
struct AVLTreeNode
{
// T-->int--T()-->int()==0 T如果是内置类型,T()是0
// T-->Date-T()-->Date() 如果T是自定义类型,该类必须提供无参的构造函数
AVLTreeNode(const T& data = T())
: _pLeft(nullptr)
, _pRight(nullptr)
, _pParent(nullptr)
, _data(data)
, _bf(0)
{}
AVLTreeNode<T>* _pLeft;
AVLTreeNode<T>* _pRight;
AVLTreeNode<T>* _pParent;
T _data;
int _bf;//保存每个结点的平衡因子
};
AVL树的插入:
1.如果是空树的话,直接插入即可。
2.如果不是空树,按照二叉搜索树的方式,插入新结点。
3.新结点插入成功后,新结点的父节点parent的平衡因子_bf需要更新。
(规定结点的平衡因子为其右子树高度减去其左子树高度)
插入新的结点,AVL树的平衡性有可能遭到破坏,所以要更新平衡因子,来检查是否破坏了平衡性。
a.如果插入在parent的左侧,只需将parent的平衡因子-1即可。
b.如果插入在parent的右侧,只需将parent的平衡因子+1即可。
此时AVL输的平衡性可能会被破坏。
a.如果插入后parent的平衡因子为0,则说明插入之前parent结点只有左子树(右子树),插入后更新为0,此时平衡性没有破坏,以parent为根的树高度没有增加,不会影响上层,所以无需继续更新。
b.如果插入后parent的平衡因子为1/-1,则说明插入之前parent结点为叶子,插入后更新为1/-1,代表此时以parent为根的树高度增加,虽然没有破坏平衡性,但是会对上层造成影响,所以需要继续向上更新平衡因子。
c.如果插入后parent的平衡因子为2/-2,说明平衡已经被破坏掉了,需要进行旋转处理,将树恢复平衡性。
旋转处理:
如果一棵树原本是AVL树,插入一个节点后破坏了树的平衡性,此时就要调整树的结构,失之再次平衡化。根据插入节点位置的不同,分为四种旋转—右单旋、左单旋、左右双旋、右左双旋。
1.右单旋
将新节点插入在较高左子树的左侧。
实现代码:
Node* pSubL = pParent->_pLeft;
Node* pSubLR = pSubL->_pRight;
pParent->_pLeft = pSubLR;
if (pSubLR)
pSubLR->_pParent = pParent;
pSubL->_pRight = pParent;
Node* pPParent = pParent->_pParent;
pSubL->_pParent = pPParent;
pParent->_pParent = pSubL;
if (nullptr == pPParent)
_pRoot = pSubL;
else
{
if (pPParent->_pLeft == pParent)
pPParent->_pLeft = pSubL;
else
pPParent->_pRight = pSubL;
}
pParent->_bf = pSubL->_bf = 0;
2.左单旋
将新节点插入在较高右子树的左侧。左单旋旋转情况可参照右单旋。
实现代码:
Node* pSubR = pParent->_pRight;
Node* pSubRL = pSubR->_pLeft;
pParent->_pRight = pSubRL;
if (pSubRL)
pSubRL->_pParent = pParent;
pSubR->_pLeft = pParent;
Node* pPParent = pParent->_pParent;
pSubR->_pParent = pPParent;
pParent->_pParent = pSubR;
if (nullptr == pPParent)
_pRoot = pSubR;
else
{
if (pParent == pPParent->_pLeft)
pPParent->_pLeft = pSubR;
else
pPParent->_pRight = pSubR;
}
pParent->_bf = pSubR->_bf = 0;
3.左右双旋
将新节点插入较高左子树的右侧,就是先左旋再右旋。
代码:
Node* pSubR = pParent->_pRight;
Node* pSubRL = pSubR->_pLeft;
int bf = pSubRL->_bf;
RotateR(pParent->_pRight);
RotateL(pParent);
// 需要更新部分节点的平衡因子
if (bf == 1)
pParent->_bf = -1;
else if (bf == -1)
pSubR->_bf = 1;