一、概念
平衡二叉树(下文称AVL)是满足每个结点的左右子树高度(亦称深度)之差在{0,1,-1}这个范围内的二叉排序树(二叉排序树也叫二叉搜索树)。
AVL的优势是在查找时避免了出现一般二叉排序树查找的最坏情况。但是AVL在经过插入或删除结点的操作之后可能出现不平衡的情况,所以每次插入或删除完之后要判断是否平衡,不平衡就需要rebalance.这会增加插入删除的复杂度。
二、AVL的创建
正常的有规律的创建方法(实际上是各种资料采用的方法)是将创建,
看成从NULL开始,一次次对原有的AVL的插入(显然只有一个结点的Tree一定是AVL)。
三、AVL的插入
1.方法:
先把结点按二叉排序树的结构正常插入,再由底向上逐级判断是否平衡,不平衡就rebalance. 插一次可能经过几次rebalance。(具体情况请往下读)
2.插入处的叶子节点的情况:
图1
显然
1) 插入处的叶子有且仅有这4种情况
2)对于②④来说,一定不影响原有树平衡
3)对于①③来说,不确定是否影响原有树的平衡;如果影响,不平衡点一定在结点A的祖先结点中(包括父亲节点,下文同)。
4)对于①③来说,我们要把这种不平衡的可能逐级传递给A的祖先结点,再逐级判断A的祖先节点 a.是否不平衡,不平衡需要rebalance,
之后b.是否有影响上一级平衡性的可能,有就需要继续传递这种可能。进行递归
3.不平衡节点的情况
!!注意:这里讨论的是三、2.4)中逐级往上,第一个出现不平衡的节点的情况。其他节点若不平衡都是依次逐级递归调整,讨论且只有讨论第一个出现的不平衡的节点的情况才有意义。
(1)插入前
图2
注:
LH:左子树高
RH:右子树高
EQ:左右子树等高
x:深度值
显然,
1)插入后第一个出现不平衡的节点在插入前有且仅有这两种情况
2)我们只需讨论其中一种情况,另一种同理可解
(本文选择第一种,下文即根据第一种而展开)
(2)插入后
图3
注:
DISEQ_LH:因为左子树过高而不平衡
DISEQ_RH:因为右子树过高而不平衡
显然
1)第一个出现不平衡的节点有且仅有这两种情况
2)有的书籍中将第一种情况称为”LL型“,第二种情况称为"LR"型
(3)对于”LL型“的讨论
图4
显然,
1)LL型有且仅有这两种情况
2)有的书籍将第一种情况称为"LL的一般型”,第二种称为“LL的简单型”,这很明显是不科学的,甚至是错误的叫法,
因为一般型不能包含简单型还叫一般吗?本文将第一种称为”LL的次简型“,第二种称为“LL的最简型”。
3)rebalance方法如下图(二者代码一致):
图5
(3)对于”LR型“的讨论
图6
显然
1)"LR型"有且仅有这两种情况
2)本文将第一种称为”LR的次简型“,第二种称为“LR的最简型”
3)rebalance方法如下图(二者代码不一致,主要在于平衡因子的改变不同):
图7
四、AVL的删除
1.方法:
1)删除:先找到要删除的结点;然后判断结点的子树情况,如果无子树,直接删除,如果有一个孩子,删除后将孩子结点接到父亲结点上,
如果有两个孩子,可以选择a.在左孩子中找到最大结点 b.在右孩子中找到最小结点 ,将找到的节点与要删除的结点的data替换,
然后问题转化为在删除结点的左子树中删除找到的结点。变成递归。
2)出现不平衡,即进行rebalance,删除一次可能rebalance多次
2.删除处的叶子节点的情况
!!注意,此处指的是四、1.1)中递归到最后删除叶子结点的情况
图8
显然,
1)删除处的叶子有且仅有这4种情况
2)对于②④来说,一定不影响原有树平衡
3)对于①③来说,不确定是否影响原有树的平衡;如果影响,不平衡点一定在结点A的祖先结点中(包括父亲节点,下文同)。
a.是否不平衡,不平衡需要rebalance, 之后b.是否有影响上一级平衡性的可能,有就需要继续传递这种可能。进行递归。
3.不平衡节点的情况
!!注意:这里讨论的是三、2.4)中逐级往上,第一个出现不平衡的节点的情况。其他节点若不平衡都是依次逐级递归调整,讨论且只有讨论第一个出现的不平衡的节点的情况才有意义。
(1)删除前
图9
显然,
1)删除后第一个出现不平衡的节点在插入前有且仅有这两种情况
2)我们只需讨论其中一种情况,另一种同理可解(本文选择第一种,下文即针对第一种情况而展开)
(2)删除后
图10
显然,
1)第一个出现不平衡的节点有且仅有这3种情况
2)对照前面的插入操作,我们知道,第一种不平衡情况称为”LL型“,第二种情况称为"LR"型,那么第三种呢?有的教材将其归为前两种之一,
那明显是错误的,请那么做的作者去看LL、LR型的定义。有的教材更差劲,甚至不讲AVL删除操作。我将第三种情况命名为“LE型”。
(3)对于”LL型“的讨论
操作同插入时的“LL型”,但请清楚具体情况与插入时不同
(4)对于”LR型“的讨论
图11
(5)对于”LE型“的讨论
图12
注:对于”LE型“,可以用"LL型"的操作旋转节点,也可以用"LR型“的操作旋转节点。(但注意过后平衡因子的改变与LL或LR不同),应用LL显然更简单。
五、关键源代码
enum BalanceFactor //平衡因子取值
{
DISEQ_RH = -2, //因为右子树高2不平衡
RH = -1, //右子树比左子树高1
EQ = 0, //左右子树等高
LH =1, //左子树比右子树高1
DISEQ_LH = 2 //因为左子树高2不平衡
};
typedef struct Tnode
{
BalanceFactor t_balancefacor;
float t_data;
struct Tnode* lchild;
struct Tnode* rchild;
}Tnode;
void createBBST(Tnode* &tree);
bool searchBBST(Tnode* tree, float search_data);
bool insertBBST(Tnode* &tree, float insert_data, bool &taller);
bool deleteBBST(Tnode* &tree, float delete_data, bool &shorter);
/*注意:AVL的插入和删除是有固定方法的,虽然还有其他手段也能使得插入后的AV