我们在上一章中已经介绍了AVL树的原理及实现,在这一章我们再通过一个例子来加深对AVL树的理解。
1. AVL树的构造
前面我们已经讲述过了AVL树的定义及相关性质,关于如何构造二叉排序树, 我们这里再给出一个例子。参看如下图:
假设表中关键字序列为(13,24,37,90,53)。空树和1个节点13
的树显然都是平衡的二叉树。在插入24之后仍是平衡的,只是根节点的平衡因子BF
由0变为-1;在继续插入37之后,由于节点13
的BF值由-1变成-2,由此出现了不平衡的现象。此时好比一根扁担出现一头重一头轻的现象,若能将扁担的支撑点由13
改至24
,扁担的两头就平衡了。由此,可以对树作一个向左逆时针“旋转”的操作, 令节点24
为根,而节点13
为它的左子树,此时, 节点13
和节点14
的平衡因子都为0, 而且保持二叉排序树的特性。在继续插入90和53之后,由于节点37
的BF值由-1变成-2,排序树中出现了新的不平衡现象,需进行调整。但此时由于节点53
插在节点90
的左子树上,因此不能如上作简单调整。对于以节点37
为根的子树来说,既要保持二叉排序树的特性,又要平衡,则必须以节点53
作为根节点,而使节点37
成为它的左子树的根,节点90
成为它的右子树的根。这好比对树作了两次“旋转”操作———先向右顺时钟,后向左逆时钟(见上图(f)~(h)), 使二叉排序树由不平衡转化为平衡。
一般情况下,假设由于二叉排序树上插入节点而失去平衡的最小子树根节点的指针为x
(即 x 是离插入节点最近, 且平衡因子绝对值超过1的祖先节点),则失去平衡后进行调整的规律可归纳为下列4种情况:
(1) 情形1
单向右旋平衡处理: 由于在*x
的左子树根节点的左子树上插入节点,*x
的平衡因子由1增至2, 致使以*x
为根的子树失去平衡,则需进行一次向右的顺时针旋转操作。 此种情形就是所谓的LL型
。
(2) 情形2
单向左旋平衡处理: 由于在*x
的右子树根节点的右子树上插入节点, *x
的平衡因子由-1变为-2,致使以*x
为根节点的子树失去平衡,则需进行一次向左的逆时钟旋转操作。此种情形就是所谓的RR型
。
(3) 情形3
双向旋转(先左后右)平衡处理: 由于在*x
的左子树根节点的右子树上插入节点,*x
的平衡因子由1增至2, 致使以*x
为根节点的子树失去平衡,则需进行两次旋转(先左旋后右旋)操作。此种情形就是所谓的LR型
。
(4) 情形4
双向旋转(先右后左)平衡处理: 由于在*x
的右子树根节点的左子树上插入节点, *x
的平衡因子由-1变为-2, 致使以*x
为根节点的子树失去平衡,则需进行两次旋转(先右旋后左旋)操作。此种情形就是所谓的RL型
。
上述4种情况中,(1)和(2)对称,(3)和(4)对称。旋转操作的正确性容易由“保持二叉排序树的特性: 中序遍历所得的关键字序列自小至大有序”证明之。由上面示意图可见,无论哪一种情况,都可以通过旋转达到平衡。因此,当平衡的二叉排序树因插入节点而失去平衡时,仅需对最小不平衡子树进行平衡旋转处理即可。因为经过旋转处理之后的子树深度和插入之前相同, 因而不影响插入路径上所有祖先节点的平衡度。
2. AVL树插入算法
在平衡的二叉排序树BBST
上插入一个新的数据元素e的递归算法可描述如下:
(1) 若BBST为空树,则插入一个数据元素为e的新节点作为BBST的根节点,树的深度增1;
(2) 若e的关键字和BBST的根节点的关键字相等,则不进行插入;
(3) 若e的关键字小于BBST的根节点的关键字,而且在BBST的左子树中不存在和e有相同关键字的节点,则将e插入在BBST的左子树上,并且当插入之后的左子树深度增加(+1)时,分别就下列不同情况处理之:
- BBST的根节点的平衡因子为-1(右子树的深度大于左子树的深度): 则将根节点的平衡因子更改为0,BBST的深度不变;
- BBST的根节点的平衡因子为0(左、右子树的深度相等): 则将根节点的平衡因子更改为1,BBST的深度增加1;
- BBST的根节点的平衡因子为1(左子树的深度大于右子树的深度): 若BBST的左子树根节点的平衡因子为1,则需进行单向右旋平衡处理,并且在右旋处理之后,将根节点和其
右子树
根节点的平衡因子更改为0,树的深度不变; 若BBST的左子树根节点的平衡因子为-1,则进行先向左、后向右的双向旋转平衡处理,并且在旋转处理之后,修改根节点和其左、右子树根节点的平衡因子,树的深度不变;
(4) 若e的关键字大于BBST的根节点的关键字,而且在BBST的右子树中不存在和e有相同关键字的节点,则将e插入在BBST的右子树上,并且当插入之后的右子树深度增加(+1)时,分别就不同情况处理之。其处理操作和(3)中所述相对称。
3. AVL树实现示例
3.1 相关头文件
头文件avltree.h
:
3.2 源文件
源文件avltree.c
:
3.3 BBST树节点的删除
这里我们仿照于插入,可以写出删除的源代码:
3.4 BBST树的测试
编译测试:
# gcc -o avltree_test avltree.c avltree_test.c
# ./avltree_test
1 2 3 4 5 6 7 8 9 10
1 3 4 5 6 7 9 10
[参看]:
-
AVL树原理及实现
-
AVL树