AVL树的调整整体分为两种:
- 单旋转
- 左单旋
- 右单旋
- 双旋转
- 先左单旋,再右单旋
- 先右单旋,再左单旋
例如:上图想要将值为40的结点插入到AVL树中,只能将其插入到值为50的左孩子结点。
这里的平衡因子为:右孩子树的高度减去左孩子树的高度。
由于AVL树的平衡因子为-1或1.因此,上图已经不满足AVL树的要求,所以要对其进行旋转调整。
单旋
一:右单旋
如何判断AVL树需要进行右单旋操作:新节点插入较高左子树的左侧,需要对其进行右单旋。
什么是较高左子树的左侧:
上图中,左子树的高度为2,右子树的高度为1,所以当新结点插入左子树的左孩子下方即称为较高左子树的左侧。
上述两种情况统称为较高左子树的左侧。
如何调整:
上图中,结点80的平衡因子,已经不满足要求,需要将80的平衡因子降低(平衡因子是左右子数高度差的绝对值,为了方便描述,我这里的平衡因子是用右子数的高度减去左子树的高度,这里说的降低平衡因子,是是降低其绝对值)。解决办法是:提较高子树的根结点。上图中较高子树的根结点为60
在调成期间也需要考虑一些特殊的情况:
例如这种单支的情况,也满足右旋条件。
**注意:**在进行指向调整的同时,记得双亲结点的调整,最终记得调整每个结点的平衡因子。
//结合上图,Node是AVL树每个结点的结构体
void RotateR(Node* pParent) //右单旋,右侧的树高度降低
{
Node* pSubL = pParent->_pLeft;
Node* pSubLR = pSubL->_pRight;
Node* pPParent = pParent->_pParent;
pParent->_pLeft = pSubLR;
if (pSubLR)
pSubLR->_pParent = pParent;
pSubL->_pRight = pParent;
pSubL->_pParent = pPParent;
pParent->_pParent = pSubL;
if (pPParent == nullptr)
{
_pRoot = pSubL;
}
else
{
if (pPParent->_pLeft == pParent)
pPParent->_pLeft = pSubL;
else if (pPParent->_pRight == pParent)
pPParent->_pRight = pSubL;
}
pParent->_bf = pSubL->_bf = 0;
}
左单旋
左单旋:左子树高度降低,类似于右单旋的镜像。
什么情况下需要进行左单旋:插入结点插在较高右子树的右侧。
解决办法:将较高右子树的根结点往上提。
调整方法:
注意:
void RotateL(Node* pParent) //左单旋,左侧高度降低
{
Node* pSubR = pParent->_pRight;
Node* pSubRL = pSubR->_pLeft;
Node* pPParent = pParent->_pParent;
pParent->_pRight = pSubRL;
if (pSubRL)
pSubRL->_pParent = pParent;
pSubR->_pLeft = pParent;
pSubR->_pParent = pPParent;
pParent->_pParent = pSubR;
if (pPParent == nullptr)
{
_pRoot = pSubR;
}
else
{
if (pPParent->_pLeft == pParent)
pPParent->_pLeft = pSubR;
else
pPParent->_pRight = pSubR;
}
pSubR->_bf = pParent->_bf = 0;
}
双旋
先右单旋,再左单旋
场景:插入结点在,较高右子树的左侧。
上述两种情况都满足,插入结点在较高右子树的左侧。
经过调整发现调整后的树,不满足AVL树的性质,但是满足AVL树,左单旋的条件,接着对其进行左单旋。
先左单旋,再右单旋
插入结点在较高左子树的右侧。
调整过程: