需要云服务器等云产品来学习Linux的同学可以移步/-->腾讯云<--/-->阿里云<--/-->华为云<--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。
目录
一、平衡二叉树的介绍
二、平衡二叉树的插入
1、平衡二叉树的插入步骤
2、平衡二叉树的旋转
2.1左单旋
2.2右单旋
2.3左右双旋
2.4右左双旋
三、平衡二叉树的删除(略)
四、个人对平衡二叉树见解
五、平衡二叉树整体代码
一、平衡二叉树的介绍
的目的是为了提高查找效率,但如果数据有序或者接近有序,那么二叉搜索树将会变成单支树,查找元素的效率等效为顺序表,树形结构的优势荡然无存。
为了解决这一问题,苏联数学家G.M.Adelson-Velskii和E.M.Landis便发明了平衡二叉树(AVL树)。
平衡二叉树:在一棵搜索二叉树中每个节点的左右子树的高度差的绝对值不超过1。左右子树的高度差被称为平衡因子(平衡因子=右子树高度-左子树高度)。若一颗平衡二叉树的节点个数为n,那么其高度为logN。
二、平衡二叉树的插入
1、平衡二叉树的插入步骤
平衡二叉树的插入第一步和二叉搜索树一样,根据二叉搜索树的特性,找到新插入节点位于整棵树的位置。
随后使用逻辑语句判断新节点是插入在父节点的左还是右,并维护其与父节点的指针关系。
新增在右,平衡因子++;新增在左,平衡因子--。那新插入了一个节点,原先的平衡二叉树的结构可能会遭到破坏,所以需要观察平衡因子的三种情况,进行分类讨论:如图
情况三如何旋转?无非是四种情况:
2、平衡二叉树的旋转
当出现上图情况三时,就需要对平衡二叉树的节点进行旋转,旋转的目的是要让这颗树继续维持平衡二叉树的形态,同时调节子树的高度,让其和插入前的高度保持一致,旋转后不要忘记更新那些被旋转的节点的平衡因子。
2.1左单旋
5可能是根,也可能是某颗子树的根,分类讨论:
void RotateLeft(Node* parent)//左单旋
{
Node* grandfather = parent->_parent;
Node* cur = parent->_right;
if (parent == _root)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (grandfather->_left == parent)//需要判定parent原来属于grandfather的哪一边
grandfather->_left = cur;
else
grandfather->_right = cur;
cur->_parent = grandfather;
}
parent->_right = cur->_left;
if (cur->_left != nullptr)
cur->_left->_parent = parent;
cur->_left = parent;
parent->_parent = cur;
//更新平衡因子
cur->_bf = parent->_bf = 0;
}
2.2右单旋
void RotateRight(Node* parent)//右单旋
{
Node* grandfather = parent->_parent;
Node* cur = parent->_left;
if (parent == _root)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (grandfather->_left == parent)
{
grandfather->_left = cur;
cur->_parent = grandfather;
}
else
{
grandfather->_right = cur;
cur->_parent = grandfather;
}
}
parent->_parent = cur;
parent->_left = cur->_right;
if (cur->_right != nullptr)
cur->_right->_parent = parent;
cur->_right = parent;
//更新平衡因子
cur->_bf = parent->_bf = 0;
}
2.3左右双旋
void RotateLR(Node* parent)//左右双旋
{
Node* cur = parent->_left;
Node* curR = cur->_right;
int bf = curR->_bf;
RotateLeft(parent->_left);
RotateRight(parent);
if (bf == -1)//curR的左树插入新节点
{
cur->_bf = 0;
parent->_bf = 1;
curR->_bf = 0;
}
else if (bf == 1)//curR的右树插入新节点
{
cur->_bf = -1;
parent->_bf = 0;
curR->_bf = 0;
}
else if (bf == 0)//curR自身为新增节点
{
cur->_bf = 0;
parent->_bf = 0;
curR->_bf = 0;
}
else
assert(false);//不可能出现这种情况
}
2.4右左双旋
void RotateRL(Node* parent)//右左双旋
{
Node* cur = parent->_right;
Node* curL = cur->_left;
int bf = curL->_bf;
RotateRight(parent->_right);
RotateLeft(parent);
if (bf == -1)//curL的左树插入新节点
{
cur->_bf = 1;
parent->_bf = 0;
curR->_bf = 0;
}
else if (bf == 1)//curL的右树插入新节点
{
cur->_bf = 0;
parent->_bf = -1;
curR->_bf = 0;
}
else if (bf == 0)//curL自身为新增节点
{
cur->_bf = 0;
parent->_bf = 0;
curR->_bf = 0;
}
else
assert(false);//不可能出现这种情况
}
三、平衡二叉树的删除(略)
按照二叉搜索树的方式对平衡二叉树节点进行删除。更新平衡因子时,平衡因子为1或-1便可以停止向上更新。当平衡因子绝对值大于1时,同样需要进行旋转解决。
四、个人对平衡二叉树见解
平衡二叉树强就强在通过大量的旋转控制整颗树任意一个节点的左右子树高度差不大于1,使树的结构近似完全二叉树,搜索效率为logN。
但偏偏是频繁的旋转,导致其插入删除的效率并不及红黑树,这也是红黑树成为树形容器的原因。
但是一颗树仅用来查找而不进行删除的话,用平衡二叉树还是很棒的。
五、平衡二叉树整体代码
#pragma once
#include
#include
#include