AVL树
1.首先AVL树是一个二叉搜索树
2.其次AVL树在高度上平衡,也就是说:树的左右子树的高度差不超过1,树的左子树和右子树的高度也不超过1。
AVL树的原理:
在二叉搜索树的基础上,每当插入一个节点,更新他的平衡因子,如果平衡因子没有违反规则,则插入结束,否则通过"旋转"操作,实现平衡同时不破坏搜索树的条件。
如果在某个位置插入节点,只会影响他的祖先的平衡因子(父亲,父亲的父亲,....)
AVL树的实现
AVL树我们重点实现他的插入,因为删除的实现与插入的实现类似,而查找的实现与二叉搜索树中的查找类似。
由于AVL树本身是一种二叉搜索树,因此其在实现上与二叉搜索树有很多相近之处。
由于需要对更新平衡因子,对不平衡节点旋转操作,所以我们维护一个parent指针指向当前节点的父亲节点,我们还要给每个节点新增一个变量存储平衡因子的值。
插入一个节点时,我们首先不考虑AVL树的平衡问题,像二叉搜索树一样,通过大小的比较,找到合适插入的空位置,然后插入该节点。这时,由于新结点的插入,该节点的祖先节点的平衡因子可能会改变,二叉树的平衡结构可能被破坏,因此我们首先更新其祖先节点的平衡因子。
平衡因子的更新
在父亲节点处插入节点,插入的是左节点平衡因子就-1,右节点平衡因子就+1
如果插入后父亲节点的平衡因子为0,证明原来其只有一个左节点或右节点,插入后弥补了空缺,不影响平衡。
如果插入后父亲的平衡因子为1/-1,证明原来其没有子节点,插入了一个子节点导致平衡因子加或者减1,这时导致了其高度的改变,并且可能影响了父亲节点的祖先节点,需要继续向上更新平衡因子:如果父亲节点是父亲节点的父亲节点的右/左,就把父亲节点的父亲节点的平衡因子加/减1,如果运算后其平衡因子为0,则判断结束,因为不会继续向上影响,如果为1/-1,则继续向上更新平衡因子,如果为2/-2,则说明在此节点的位置开始不平衡,则对此节点进行旋转操作,操作后二叉树平衡。循环往复。
bool insert(const pair<K,V>& kv)
{
if (_root == nullptr)//插入节点是根节点
{
_root = new Node(kv);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(kv);
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
cur->_parent = parent;
}
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)//高度改变继续向上找不平衡节点
{
cur = parent;
parent = parent->_parent;
}
else if (parent->_bf == 2 || parent->_bf == -2)//找到不平衡节点
{
//旋转处理
if (parent->_bf == 2)
{
if (cur->_bf == 1)
{
RotateL(parent);//左单旋
}
else if (cur->_bf == -1)
{
RotateRL(parent);//右左双旋
}
}
else if (parent->_bf == -2)
{
if (cur->_bf == -1)
{
RotateR(parent);//右单旋
}
else if (cur->_bf == 1)
{
RotateLR(parent);//左右双旋
}
}
break;
}
}
return true;
}
AVL树的重点和难点是在不平衡节点处通过旋转操作,使整个二叉树变平衡
旋转操作可以根据情况不同细分为:左单旋,右单旋,左右双旋,右左双旋
单旋的路径为直线,双旋的路径为折线
当插入节点在较高右子树的右侧时,进行左单旋:
subR的左给到parent的右,parent变成subR的左
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
subR->_left = parent;
Node* ppNode = parent->_parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (ppNode->_left == parent)
ppNode->_left = subR;
else
ppNode->_right = subR;
subR->_parent = parent->_parent;
}
parent->_bf = subR->_bf = 0;
}
当新插入节点在较高左子树的左侧时,进行右单旋:
subL的右给到parent的左,parent变成subL的右
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* ppNode = parent->_parent;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
parent->_parent = subL;
subL->_right = parent;
if (parent == _root)
{
_root == subL;
subL->_parent == nullptr;
}
else
{
if (ppNode->_left == parent)
ppNode->_left == subL;
else
ppNode->_right == subL;
subL->_parent = ppNode;
}
parent->_bf = subL->_bf = 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)
{
parent->_bf = 0;
subL->_bf = -1;
subLR->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 1;
subL->_bf = 0;
subLR->_bf = 0;
}
else if (bf == 0)
{
parent->_bf = 0;
subL->_bf = 0;
subLR->_bf = 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)
{
parent->_bf = 0;
subR->_bf = 1;
subRL->_bf = 0;
}
else if (bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
subRL->_bf = 0;
}
else if (bf == 0)
{
subR->_bf = 0;
parent->_bf = 0;
subRL->_bf = 0;
}
}