前言
接着上一节的二叉搜索树,我们今天分析平衡搜索树的一种——AVL搜索树。本篇文章暂时不涉及代码,专注于插入和删除操作的单个逻辑,请谅解。
AVL树与AVL搜索树
为什么要学习AVL搜索树?
请见这里的二叉搜索树部分。
AVL树:一颗空的二叉树是AVL树;如果T是一颗非空二叉树,当T的左子树和右子树都为AVL树且两子树的高度相差不超过1时,T为AVL树。
AVL搜索树:当一颗树既是二叉搜索树又是AVL树时,它是AVL搜索树。
AVL搜索树各操作
在学习各操作之前,需要理解平衡因子
的概念。在AVL树中,每个节点都有一个代表平衡因子的int类型。
某节点x的平衡因子 = x的左子树高度 - x的右子树高度
由AVL树的定义可知,平衡因子bf(x)的取值只能是-1
、0
、1
。
AVL搜索树的搜索(find)
AVL搜索树的搜索操作可以完全继承上一节的二叉搜索树的搜索方法,可以点击这里进行查看。
接下来我们分析为什么AVL树的搜索的时间复杂度为O(logn)。
对一颗高度为h的AVL树,令Nh为其最少的节点数。由AVL树的递归定义可知:
Nh = N(h-1) + N(h-2) + 1, N0 = 0 且 N1 = 1
而Nh的定义与斐波那契数列的定义相似:
Fn = F(n-1) + F(n-2),F0 = 0且F1 = 1
他们有以下关系:Nh = F(h+2) - 1
斐波那契的通项公式为:
当n逐渐增大,后面((1-√5)/2)^n这一项无限接近于零,我们可认为Fh约等于
对[a(h+2) - 1]取对数,解出h ≈ 1.44 * log(n+2) = O(logn)
AVL搜索树的插入(insert)
因为AVL搜索树按照二叉搜索树的规则插入后,有可能会出现不符合AVL树的情况。为了维护AVL树的性质,在插入新节点后,我们要对其进行做旋转操作。
我们在判断旋转怎样进行时,其实只需要关注三个节点。
- 节点X:插入新节点(未旋转)之后,平衡因子是2或-2的离插入节点最近的祖先。
- 节点A:在节点X和新插入节点之间的路径上,离节点X最近的孩子节点。
- 节点S:在节点X和新插入节点之间的路径上,离节点A最近的孩子节点(一般为新插入节点)。
AVL搜索树的插入分为四种情况,LL,RR,RL,LR。其中LL、RR称为单旋转,RL、LR称为双旋转,双旋转即一次插入做两次单旋转。
如何判断该插入节点后的结构是哪种情况呢?
- 观察法(不够靠谱,熟练的时候可以用。)
- 根据节点X和节点A的平衡因子(绝对靠谱!)
如何判断需要旋转的情况下的旋转轴呢?(旋转嘛,肯定有一个节点作为轴)
- 如果是LL、RR类型,旋转轴为节点X。
- 如果是RL、LR类型,两次旋转的旋转轴均为节点S。
上图!
下面进行对LL和LR进行详细分解
LL:
LR:
OK!理论学习完成我们下一步看两个难度略高的例子
我将例子和答案分开展示,可以自己先试一下。
例一:
解析:
例二:
解析:
AVL搜索树的删除(erase)
与插入一样,删除也可能使树的结构不符合AVL树的性质,且分为六种情况。
AVL搜索树的删除分为两步:
-
第一步,参照二叉搜索树的删除方法进行删除。删除后的结构可能不符合AVL树性质。
-
第二步,使用旋转操作使其平衡。与插入方法不同的是,这里使用一次旋转可能无法恢复平衡。所需要的旋转次数为O(logn)。
一开始我有些疑惑,需要旋转的结构只有RR、RL、LL、LR四种情况,为什么在AVL树的删除中要分6种情况呢?
在解惑之前我们需要了解和删除有关的三个关键节点:
- 节点S:被删除的节点
- 节点Q:被删除的节点的父节点(这里需要注意,与二叉搜索树的删除方法有关)
- 节点A:从节点Q到根节点的路径上,自底向上第一个平衡因子变为-2或2的节点。
下面我们说说节点Q,理解节点Q首先需要理解二叉搜索树的删除方法。
假如在上图中删除节点25,那么节点Q便是节点20(根节点)。
假如在上图中删除节点12,那么节点Q便是删除后节点12的位置(根节点的左孩子)。
原理和二叉搜索树中删除节点有一个孩子还是两个孩子有关,所以想要理解节点Q必须理解二叉搜索树的删除方法,感觉我已经有点啰嗦了。
理解这几个节点之后,我们分析R0,R1, R-1, L0, L1, L-1六种情况。因为前三种和后三种是对称关系,所以我们只分析前三种。
R0:
R1:
R-1:
解析:
图中的节点A即为上述提到的关键点A。得到关键点A后,不平衡情况便可按照添加方法中的节点X对待。细心的同学可以发现R0和R1其实都是LL类型,唯一不同之处为R0旋转后高度不变,R1旋转后高度-1。
我们可以想象一种情况,节点A的父结点的平衡因子为1,A是其父节点的右子结点。
- R0的情况下,旋转后父结点的平衡因子仍为1,一次旋转即可平衡。
- R1的情况下,旋转后父结点的平衡因子为2,结构不平衡,需要将父结点看作下一个结点A,继续进行旋转。因为AVL平衡树的高度为 O ( l o g n ) O(logn) O(logn)。旋转可能持续到根节点才结束,所以一次删除操作R1情况下最多需要的旋转次数为 O ( l o g n ) O(logn) O(logn)。
- R-1的情况下,为LR类型,且旋转后高度 - 1。
- 删除可分为6种类型,因为把旋转后的高度变化考虑了进去。