## 什么是平衡二叉树?
平衡二叉树(Balanced Binary Tree),由前苏联数学家[G. M. Adelson-Velsky](https://zh.wikipedia.org/wiki/格奥尔吉·阿杰尔松-韦利斯基)和[Evgenii Landis](https://zh.wikipedia.org/w/index.php?title=Evgenii_Landis&action=edit&redlink=1)在他们1962年的论文《An algorithm for the organization of information》中提出,因此也叫AVL树。
它是具有如下性质的树:
1. 可以是一棵空树
2. 可以是一颗左子树和右子树都是平衡二叉树且左右子树的深度之差的绝对值不超过1。
## 简单理解平衡二叉树
上文也出现了递归定义,可以不严谨的理解为:要么是一棵空树,要么是一棵平衡二叉树,在这棵树中,以任意节点为根作为一棵新树,那么这棵新树也得是二叉平衡树。
二叉平衡树是对二叉搜索树的改进,为什么要改进二叉搜索树?
假如有一棵二叉搜索树(BST),且为一棵空树。
当我们插入一组升序数据[1, 3, 5, 7, 9],BST1是这样的:
![https://lookcos.cn/usr/uploads/2022/01/253342560.png](https://lookcos.cn/usr/uploads/2022/01/253342560.png)
<center>(图1)</center>
当我们插入一组降序数据[10, 8, 6, 4, 2],BST2是这样的:
![https://lookcos.cn/usr/uploads/2022/01/2188928686.png](https://lookcos.cn/usr/uploads/2022/01/2188928686.png)
<center>(图2)</center>
那么请问这两棵树是二叉搜索树吗?的确是,完全符合二叉搜索树的性质。但毫无疑问,看上去更像是个链表。确实,当插入一组有序数据时,二叉搜索树退化成了链表,此时的查找效率退化为O(N)。
## 平衡因子
平衡因子(Balance Factor, BF)是某个节点左子树和右子树的高度之差(有的说法也叫深度之差,都是表达一个意思),一棵平衡二叉树的所有节点的平衡因子都只可能是0、-1、1。这一点也可以用来验证一棵二叉树是否为平衡二叉树。因为只要某二叉树的某个节点平衡因子大于1,那么该二叉树就一定不会是平衡二叉树。
如下由一棵平衡二叉树:
![https://lookcos.cn/usr/uploads/2022/01/3398053130.png](https://lookcos.cn/usr/uploads/2022/01/3398053130.png)
<center>(图3)</center>
其中H表示高度(height),L表示左子树高度(Left height),R表示右子树高度(Right height), BF是平衡因子(Balance Factor, BF=L-R)。
如下有一棵非二叉平衡树:
![https://lookcos.cn/usr/uploads/2022/01/2734824297.png](https://lookcos.cn/usr/uploads/2022/01/2734824297.png)
<center>(图4)</center>
其中根节点的平衡因子绝对值大于1,出现了“失衡”。
## 从非平衡二叉树到平衡二叉树
如何创建一棵平衡二叉树呢?由于平衡二叉树是对二叉搜索树的改进,所以在做插入操作的时候,我们仍然按照二叉搜索树的方式:找到要插入的位置并进行插入。然后如果该树中出现了平衡因子绝对值大于1的节点,就说明该调整了。我们把离插入节点最近且平衡因子绝对值大于1的祖先节点,以该节点为根的子树称为**最小不平衡子树**。首先要调整的就是这棵子树。
假如我们要插入一组数据[2, 1, 0]
相继插入,2和1后,树是这样的:
![https://lookcos.cn/usr/uploads/2022/01/1979666009.png](https://lookcos.cn/usr/uploads/2022/01/1979666009.png)
<center>(图5)</center>
此时,并没有出现不平衡的情况,可以看作是一棵平衡二叉树。
但是插入0之后,就是图4那样了,根节点的平衡因子为2。我们以根节点2为根节点的树叫做**最小不平衡子树**,情况特殊,因为此时根节点出现了失衡,所以最小不平衡子树是它本身。
如下图的一棵非二叉平衡树,
![https://lookcos.cn/usr/uploads/2022/01/3904874642.png](https://lookcos.cn/usr/uploads/2022/01/3904874642.png)
<center>(图6)</center>
可以看到,在插入节点53之后,节点24和节点37的BF绝对值都大于1,但我们把以节点37为根的树叫做**最小不平衡子树**,因为它离刚插入的节点53最近。
## 平衡二叉树与其节点的表示
```c
/* 平衡二叉树的节点 */
typedef struct avl_node {
/* 关键字 */
int key;
/* 左右节点指针 */
struct avl_node *left;
struct avl_node *right;
/* 节点的高度 */
int height;
}avl_node;
/* 平衡树二叉树本身 */
typedef struct avl {
/* 指向二叉树根节点 */
struct avl_node *root;
/* 树的节点数 */
int size;
}avl;
```
## 平衡二叉树及其节点的创建
```c
/* 创建一个节点 */
avl_node *avl_create_node(int key)
{
avl_node *node = (struct avl_node*)malloc(sizeof(struct avl_node));
if(node==NULL) return NULL;
node->height = 1;
node->key = key;
node->left = NULL;
node->right = NULL;
return node;
}
/* 创建一棵平衡二叉树 */
avl *avl_create(void)
{
avl *avl = (struct avl*)malloc(sizeof(struct avl));
if(avl==NULL) return NULL;
avl->root = NULL;
avl->size = 0;
return avl;
}
```
## 节点的高度相关函数
```c
/* 返回一个节点的高度 */
int avl_height(avl_node *node)
{
if(node==NULL) return 0;
return node->height;
}
/* 比较一个key和一个节点的key的大小
* 如果key比较大,返回真,否则返回假。
*/
int avl_compare_key(int key, avl_node *node)
{
if(node==NULL) return 0;
if(key > node->key) return 1;
return 0;
}
/* 返回两个节点中的高度较大值 */
int max(avl_node *x, avl_node *y)
{
int m = avl_height(x);
int n = avl_height(y);
return m>=n?m:n;
}
```
### 四种不平衡情况
一棵平衡二叉树在插入某个节点后失衡,只会出现四种不平衡类型。在每种不平衡类型中在处理不平衡的操作中,顺时针旋转,叫做右旋;逆时针旋转叫做左旋。
### LL型
LL型可以看作是重心在Left,需要进行一次右旋操作。
```c
/* LL型,需要向右旋 */
avl_node *avl_rotate_ll(avl_node *node)
{
/* 新的“根” */
avl_node *top = node->left;
node->left = top->right;
top->right = node;
/* 重新计算高度, 注意:node与top的高度计算次序千万不能颠倒 */
node->height = max(node->left, node->right) + 1;
top->height = max(top->left, top->right) + 1;
return top;
}
```
下图演示了最小不平衡子树是它本身(简称情况1):
找到最小不平衡子树(根节点31)后右旋。我们把最小不平衡子树的左子树作为新的top节点。
![https://lookcos.cn/usr/uploads/2022/01/2710011022.gif](https://lookcos.cn/usr/uploads/2022/01/2710011022.gif)
下图演示了最小不平衡子树的根不是二叉树的根,也即最小不平衡子树是二叉树的“真子树”(情况2)。类似于集合中的真子集。
![https://lookcos.cn/usr/uploads/2022/01/1195782005.gif](https://lookcos.cn/usr/uploads/2022/01/1195782005.gif)
### RR型
```c
/* RR型,需要向左旋 */
avl_node *avl_rotate_rr(avl_node *node)
{
avl_node *top = node->right;
node->right = top->left;
top->left = node;
/* 重新计算高度 */
node->height = max(node->left, node->right) + 1;
top->height = max(top->left, top->right) + 1;
return top;
}
```
情况1:
![https://lookcos.cn/usr/uploads/2022/01/335641130.gif](https://lookcos.cn/usr/uploads/2022/01/335641130.gif)
情况2:
![https://lookcos.cn/usr/uploads/2022/01/3773463504.gif](https://lookcos.cn/usr/uploads/2022/01/3773463504.gif)
### LR型
假如对空树AVL插入一组数据[31, 25, 28]
相继插入31,25后没问题,接着插入28:
![https://lookcos.cn/usr/uploads/2022/01/310830524.png](https://lookcos.cn/usr/uploads/2022/01/310830524.png)
此时出现了以节点31为根节点的不平衡子树,既不是LL型,也不是RR型,而是LR型。需要进行两次操作,第一次对根节点的左子�
辣椒种子
- 粉丝: 4333
- 资源: 5837
最新资源
- 基于MCGS昆仑通态触摸屏与三菱变频器联动的双机多段速控制系统实时调控方案,MCGS昆仑通态触摸屏与2台三菱变频器多段速控制系统可直接应用与现场的控制系统 目标:通过MCGS昆仑通态触摸屏与三菱变频
- 西门子S7-1200控制V90伺服系统:两轴移载机控制程序与原点回归及参数设置指南,西门子S7-1200通过P N控制V90伺服 1.1200两轴移载机控制程序; 2.1200控制V90回原点程序;
- 音乐标签 1.2.5.2.apk
- liblockfile-1.08-17.el7.x64-86.rpm.tar.gz
- liblockfile-devel-1.08-17.el7.x64-86.rpm.tar.gz
- 无锡某大厂成熟的Foc电机控制代码:支持双模切换、多种保护及功能,基于Stm32F030,用于高端电动车,实物板子可调试 ,无锡某大厂成熟Foc电机控制 代码,有原理图,用于很多电动车含高端电动自行车
- liblognorm-2.0.2-3.el7.x64-86.rpm.tar.gz
- Python实现UDS通信脚本:支持Vector CAN与PCAN,适用于新能源电动汽车行业资深工程师的专业工具集,Python实现的UDS通信脚本 Python实现的UDS通信脚本,支持Vector
- liblognorm-devel-2.0.2-3.el7.x64-86.rpm.tar.gz
- liblognorm-doc-2.0.2-3.el7.x64-86.rpm.tar.gz
- liblognorm-utils-2.0.2-3.el7.x64-86.rpm.tar.gz
- liblouis-2.5.2-12.el7-4.x64-86.rpm.tar.gz
- liblouis-devel-2.5.2-12.el7-4.x64-86.rpm.tar.gz
- 基于深度学习和单目摄像头测距的前车碰撞预警系统源码解析与GPU&CPU版本需求说明,前车碰撞预警-FCW,基于深度学习和单目摄像头测距的前车碰撞预警源码 单目测距,多目标跟踪 车辆检测,智能ad
- liblouis-doc-2.5.2-12.el7-4.x64-86.rpm.tar.gz
- "Yank二五O5m步进驱动器全C源程序:可移植与二次开发指南",yank 二五O5m步进驱动器全c源程序,可移植可二次开发 ,yank; 二五O5m步进驱动器; 全c源程序; 可移植; 可二次开发
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈