一.AVL树的旋转
由于在插入新节点后可能会导致双亲的平衡因子变为2或-2,此时为了保证该树依然是AVL树,则要进行旋转;我们将AVL树的旋转分为左单旋,右单旋,左右双旋,右左双旋四种情况
情况一:左单旋
如果新节点插入在较高右子树的右侧,此时进行左单旋
在节点16的右侧插入节点后,根节点的平衡因子变为2,此时进行左单旋(降低右子树高度)
思想: parent变为subR的左子树,subRL变为parent的右子树
步骤:
- 要旋转的节点为parent,subR为parent的右孩子,subRL为subR的左孩子
- parent的右孩子指向subRL,如果subRL不为空,则subRL的父亲指向parent;subR的左孩子指向parent,parent的双亲指向subR
- 如果parent是根节点,则subR此时变为根节点,否则让parent的双亲节点的孩子节点指向subR(要判断是左孩子还是右孩子),subR的双亲节点指向parent的双亲节点
- 更新subR和parent的平衡因子都为0
实现:
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* pparent = parent->_parent;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
subR->_left = parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (pparent->_left == parent)
pparent->_left = subR;
else
pparent->_right = subR;
subR->_parent = pparent;
}
subR->_bf = parent->_bf = 0;
}
情况二:右单旋
如果新节点插入在较高右左子树的左侧,此时进行右单旋
在节点7的左侧插入节点后,根节点的平衡因子变为-2,此时进行右单旋(降低左子树高度)
思想: parent变为subL的右子树,subLR变为parent的左子树
步骤:
- parent为要旋转的节点,subL为parent的左孩子,subLR为subL的右孩子
- parent的左孩子指向subLR,如果subLR存在,则它的双亲指向parent; subL的右孩子指向parent,parent的双亲结点指向subL;
- 如果parent是根节点,那么subL此时变为根节点;否则,让parent的双亲结点指向subL(需要判断是左孩子还是右孩子),subL的双亲节点指向parent的双亲节点
- 更新subL和parent的平衡因子为0
实现:
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* pparent = parent->_parent;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (pparent->_left == parent)
pparent->_left = subL;
else
pparent->_right = subL;
subL->_parent = pparent;
}
subL->_bf = parent->_bf = 0;
}
情况三:右左双旋
如果新节点插入在较高右子树的左侧,则先进行右单旋,再进行左单旋
在节点20的左侧插入节点后,节点7的平衡因子变为2,此时先对节点7的右子树进行右单旋,再对节点7进行左单旋
思想 :先对subR进行右单旋,再对parent进行左单旋
步骤:
- 先对subR进行右单旋,保存subRL的平衡因子
- 再对parent进行左单旋
- 如果subRL的平衡因子为-1,则证明在它的左侧插入新节点,更新subR的平衡因子为1,parent的平衡因子为0
- 如果subRL的平衡因子为1,则证明在它的右侧插入新节点,更新subR的平衡因子为-1,parent的平衡因子为-1
- 更新subRL的平衡因子为0
实现:
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
if (bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
subR->_bf = 1;
parent->_bf = 0;
}
else if (bf == 0)
{
subR->_bf = parent->_bf = 0;
}
subRL->_bf = 0;
}
情况四:左右双旋
如果新节点插入在较高左子树的右侧,则先进行左单旋,再进行右单旋
在节点15的左侧插入节点后,节点20的平衡因子变为-2,此时先对节点20的左子树进行左单旋,再对节点20进行右单旋
**思想:**先对subL进行左单旋,再对parent进行右单旋
步骤:
- 先对subL进行左单旋,保存subLR的平衡因子
- 再对parent进行右单旋
- 如果subLR的平衡因子为-1,则证明在它的左侧插入新节点,更新subL的平衡因子为0,parent的平衡因子为1
- 如果subLR的平衡因子为1,则证明在它的右侧插入新节点,更新subR的平衡因子为-1,parent的平衡因子为0
- 更新subRL的平衡因子为0
实现:
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(parent->_left);
RotateR(parent);
if (bf == 1)
{
subL->_bf = -1;
parent->_bf = 0;
}
else if (bf == -1)
{
subL->_bf = 0;
parent->_bf = 1;
}
else if (bf == 0)
{
subL->_bf = parent->_bf = 0;
}
subLR->_bf = 0;
}