408答疑
文章目录
三、树形查找
平衡二叉树(AVL)
平衡二叉树的概念和性质
- AVL 树的定义:平衡二叉树可定义为或是一棵空树,或是具有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的高度差的绝对值不超过 1。
- 图示说明:
- 如下图(1)所示是平衡二叉树,图(2)所示是不平衡的二叉树。
- 结点中的数字为该结点的平衡因子。
-
AVL 树的平衡条件:任意节点的左右子树的高度差不能超过1,即 ∣ b f ∣ ≤ 1 |bf| \leq 1 ∣bf∣≤1。
-
平衡因子 b f bf bf 的计算:一般使用左树高度减去右树高度。
-
平衡因子 b f bf bf 的值:只能为 -1, 0, 1。
平衡二叉树的平衡调整
- AVL 树最重要的特点就是要保持平衡,如果树形结构不平衡,则需要调整平衡。
- 能影响 AVL 树平衡的因素只有两个,插入节点和删除节点。
- 如果插入节点或者删除节点导致 AVL 不平衡,则需要调整平衡,调整的手段为旋转节点。
- 旋转分为:单旋转(左单旋转、右单旋转)、双旋转(先左后右、先右后左)。
旋转情形
如下图所示,结点旁的数值代表结点的平衡因子,而用方块表示相应结点的子树,下方数值代表该子树的高度。
- LL 旋转(右单旋转):
- 在结点 A A A 的左孩子(L)的左子树(L)上插入了新结点, A A A 的平衡因子由 1 增至 2,导致以 A A A 为根的子树失去平衡,需要一次向右的旋转操作。
- 将 A A A 的左孩子 B B B 向右上旋转代替 A A A 成为根结点,将 A A A 向右下旋转成为 B B B 的右孩子, B B B 的原右子树则作为 A A A 的左子树。
- RR 旋转(左单旋转):
- 在结点 A A A 的右孩子(R)的右子树(R)上插入了新结点, A A A 的平衡因子由 -1 减至 -2,导致以 A A A 为根的子树失去平衡,需要一次向左的旋转操作。
- 将 A A A 的右孩子 B B B 向左上旋转代替 A A A 成为根结点,将 A A A 向左下旋转成为 B B B 的左孩子, B B B 的原左子树则作为 A A A 的右子树。
- LR 旋转(先左后右双旋转):
- 在 A A A 的左孩子(L)的右子树(R)上插入新结点, A A A 的平衡因子由 1 增至 2,导致以 A A A 为根的子树失去平衡,需要进行两次旋转操作,先左旋转后右旋转。
- 先将 A A A 的左孩子 B B B 的右子树的根结点 C C C 向左上旋转提升到 B B B 的位置,然后把结点 C C C 向右上旋转提升到 A A A 的位置。
- RL 旋转(先右后左双旋转):
- 在 A A A 的右孩子(R)的左子树(L)上插入新结点, A A A 的平衡因子由 -1 减至 -2,导致以 A A A 为根的子树失去平衡,需要进行两次旋转操作,先右旋转后左旋转。
- 先将 A A A 的右孩子 B B B 的左子树的根结点 C C C 向右上旋转提升到 B B B 的位置,然后把结点 C C C 向左上旋转提升到 A A A 的位置。
LR 和 RL 旋转时,新结点究竟是插入 C 的左子树还是插入 C 的右子树不影响旋转过程,如上图中以插入 C 的左子树中为例。
平衡二叉树的插入
插入节点的过程
- 插入节点之前,必须保证AVL树是平衡的。
- 查找新插节点的位置,新插节点一定为叶子节点,平衡因子 b f = 0 bf=0 bf=0。
- 修改父节点的平衡因子 b f bf bf。
- 考察父节点的平衡因子,有三种情况:
- 情况1:父节点 b f = 0 bf=0 bf=0,结束调整。
- 情况2:父节点 ∣ b f ∣ = 1 |bf|=1 ∣bf∣=1,向根的方向继续回溯。
- 情况3:父节点 ∣ b f ∣ = 2 |bf|=2 ∣bf∣=2,发生不平衡,判断平衡因子的符号,进行旋转调整。
- 调整对象:每次调整的对象都是最小不平衡子树,即以插入路径上离插入结点最近的平衡因子的绝对值大于 1 的结点作为根的子树,下图中虚线框内为最小不平衡子树。
平衡二叉树的构造
-
序列示例:以关键字序列 {15, 3, 7, 10, 9, 8} 构造一棵平衡二叉树的过程为例。
-
插入节点 7:下图(4)插入 7 后导致不平衡,最小不平衡子树的根为 15,插入位置为其左孩子的右子树,所以执行 LR 旋转,先左后右双旋转,调整后的结果如下图(5)所示。
-
插入节点 9:下图(7)插入 9 后导致不平衡,最小不平衡子树的根为 15,插入位置为其左孩子的左子树,所以执行 LL 旋转,右单旋转,调整后的结果如下图(8)所示。
-
插入节点 8:下图(9)插入 8 后导致不平衡,最小不平衡子树的根为 7,插入位置为其右孩子的左子树,所以执行 RL 旋转,先右后左双旋转,调整后的结果如下图(10)所示。
平衡二叉树的删除
与平衡二叉树的插入操作类似,以删除结点 w w w 为例来说明平衡二叉树删除操作的步骤:
- 用二叉排序树的方法对结点 w w w 执行删除操作。
- 若导致了不平衡,则从结点 w w w 开始向上回溯,找到第一个不平衡的结点 z z z(即最小不平衡子树); y y y 为结点 z z z 的高度最高的孩子结点; x x x 是结点 y y y 的高度最高的孩子结点。
- 然后对以
z
z
z 为根的子树进行平衡调整,其中
x
x
x、
y
y
y 和
z
z
z 可能的位置有 4 种情况:
- y y y 是 z z z 的左孩子, x x x 是 y y y 的左孩子(LL,右单旋转);
- y y y 是 z z z 的左孩子, x x x 是 y y y 的右孩子(LR,先左后右双旋转);
- y y y 是 z z z 的右孩子, x x x 是 y y y 的右孩子(RR,左单旋转);
- y y y 是 z z z 的右孩子, x x x 是 y y y 的左孩子(RL,先右后左双旋转)。
这四种情况与插入操作的调整方式一样。不同之处在于,插入操作仅需要对以 z z z 为根的子树进行平衡调整;而删除操作就不一样,先对以 z z z 为根的子树进行平衡调整,若调整后子树的高度减 1,则可能需要对 z z z 的祖先结点进行平衡调整,甚至回溯到根结点(导致树高减 1)。
以删除下图(1)的结点 32 为例,由于 32 为叶结点,直接删除即可,向上回溯找到第一个不平衡结点 44(即 z z z), z z z 的高度最高的孩子结点为 78( y y y), y y y 的高度最高的孩子结点为 50( x x x),满足 RL 情况,先右后左双旋转,调整后的结果如图(3)所示。
平衡二叉树的结点数分析
-
查找过程:在平衡二叉树上进行查找的过程与二叉排序树的相同。因此,在查找过程中,进行关键字的比较次数不超过树的深度。
-
结点数计算:假设以 n h n_h nh 表示深度为 h h h 的平衡二叉树中含有的最少结点数。显然,有 n 0 = 0 , n 1 = 1 , n 2 = 2 n_0 = 0, n_1 = 1, n_2 = 2 n0=0,n1=1,n2=2,并且有 n h = n h − 2 + n h − 1 + 1 n_h = n_{h-2} + n_{h-1} + 1 nh=nh−2+nh−1+1,如下图所示,依次推出 n 3 = 4 , n 4 = 7 , n 5 = 12 , ⋯ n_3 = 4, n_4 = 7, n_5 = 12, \cdots n3=4,n4=7,n5=12,⋯。
- 最大深度:含有 n n n 个结点的平衡二叉树的最大深度为 O ( log 2 n ) O(\log_2 n) O(log2n),因此平均查找效率为 O ( log 2 n ) O(\log_2 n) O(log2n)。
二叉排序树的查找效率分析
二叉排序树的查找效率主要取决于树的高度。以下是不同情况下二叉排序树的查找效率分析:
平衡二叉树
- 若二叉排序树的左、右子树的高度之差的绝对值不超过 1(平衡二叉树),它的平均查找长度为 O ( log 2 n ) O(\log_2 n) O(log2n)。
单支树
- 若二叉排序树是一个只有右(左)孩子的单支树(类似于有序的单链表),则其平均查找长度为 O ( n ) O(n) O(n)。
最坏情况
- 在最坏情况下,即构造二叉排序树的输入序列是有序的,则会形成一个倾斜的单支树,此时二叉排序树的性能显著变坏,树的高度也增加为元素个数 n n n,如下图所示。
- 上图 (1) 查找成功的平均查找长度为 A S L 1 = ( 1 + 2 × 2 + 3 × 4 + 4 × 3 ) / 10 = 2.9 ASL_1 = (1 + 2 \times 2 + 3 \times 4 + 4 \times 3) / 10 = 2.9 ASL1=(1+2×2+3×4+4×3)/10=2.9
- 上图 (2) 查找成功的平均查找长度为 A S L 2 = ( 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 ) / 10 = 5.5 ASL_2 = (1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10) / 10 = 5.5 ASL2=(1+2+3+4+5+6+7+8+9+10)/10=5.5
从查找过程看,二叉排序树与二分查找相似。就平均时间性能而言,二叉排序树上的查找和二分查找差不多。但二分查找的判定树唯一,而二叉排序树的查找不唯一,相同的关键字其插入顺序不同可能生成不同的二叉排序树。
就维护表的有序性而言,二叉排序树无须移动结点,只需修改指针即可完成插入和删除操作,平均执行时间为 O ( log 2 n ) O(\log_2 n) O(log2n)。二分查找的对象是有序顺序表,若有插入和删除结点的操作,所花的代价是 O ( n ) O(n) O(n)。当有序表是静态查找表时,宜用顺序表作为其存储结构,而采用二分查找实现其查找操作;若有序表是动态查找表,则应选择二叉排序树作为其逻辑结构。
六、参考资料
鲍鱼科技课件
b站免费王道课后题讲解:
网课全程班: