目录
前言
1.平衡二叉树
1.1概念与特点
1.2与二叉排序树比较
1.3判断平衡二叉树
2.平衡二叉树的构建
2.1平衡因子 BF
2.2 LL型失衡(右旋)
2.3 RR型失衡(左旋)
2.4 LR型失衡(先左旋再右旋)
2.5 RL型失衡(先右旋再左旋)
2.6构建平衡二叉树代码实现
3.删除节点操作
3.1删除叶子节点
编辑
3.2删除只有一个左(右)子树的节点
3.3删除有左右子树的节点
编辑
3.4删除节点代码实现
4.平衡二叉树相关操作
4.1查找数据
4.2判断是否为平衡二叉树
4.3中序遍历输出
4.4销毁平衡二叉树
前言
前面我发布了一篇介绍二叉排序树的相关文章,那么今天我们学习一种新的树----平衡二叉树,在此之前我们要学习过二叉排序树,才能更好的去学习平衡二叉树,平衡二叉树是二叉排序树的进一步优化,下面就一起来看看吧!
1.平衡二叉树
1.1概念与特点
在计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis,他们在1962年的论文《An algorithm for the organization of information》中发表了它。
二叉排序树相关链接:数据结构-----二叉排序树_Gretel Tade的博客-CSDN博客
特性:
- 平衡二叉树是一个二叉排序树
- 它的左子树和右子树都是平衡二叉树
- 左右子树高度之差(简称平衡因子)的绝对值不超过 1(即:-1,0,1)
如图所示:
1.2与二叉排序树比较
在此之前我们学习过了二叉排序树的相关知识点,二叉排序树又名为二叉搜索树,但是当一个二叉排序树的形态是一个链状的话,那么搜索效率跟一个链表的搜索效率是毫无区别的,但是如果是一个平衡二叉树就不同了,因为平衡二叉树规定了左右子树高度差不会大于1,这样就可以大大提高了搜索的效率,下面看比较:
当前我插入[1,2,3,4,5,6] 这一数组构建一个二叉排序树的时候,其形态是如下图所示,很明显是一个链态的,无异于一个链表,当我要去搜索数字6的时候,就要遍历到最后一个数字,搜索效率为 O(n)。
但是如果我通过构建平衡二叉树来储存这些数据的话,那就会有很大的不同,如下图所示,当我要去搜索最后一个节点的时候,就不需要去遍历这么多节点了,可以这么说,平衡二叉树是排序二叉树的优化结果。
1.3判断平衡二叉树
- 1. 是二叉排序树
- 2. 任何一个节点的左子树或者右子树都是平衡二叉树,而且左右高度差小于等于 1
(1)这个不是平衡二叉树, 因为右子树比左子树高度差大于2
(2)这个不是平衡二叉树,因为没有符合二叉排序树的要求
(3)这个是平衡二叉树,既满足二叉排序树的要求,而且左右子树高度差小于2.
2.平衡二叉树的构建
前面去构建一个二叉排序树,非常简单,只需要排序就行了,那构建一个平衡二叉树不仅仅要考虑到排序问题,还要考虑到左右子树高度不大于1才行,下面我介绍一种通过旋转的方法来去构建平衡二叉树。
2.1平衡因子 BF
定义:左子树和右子树高度差
计算方法:左子树高度减右子树高度的绝对值
当满足BF<=1的时候,这个树就满足平衡条件。当不满足平衡条件的话,那就只能去通过旋转来实现平衡。下面有4种不满足平衡条件的情况,如图所示:
树1是满足平衡条件的情况;
树2是在左子树右子节点出现失衡情况,属于 LR型
树3是在右子树右子节点出现失衡情况,属于 RR型
树4是在左子树左子节点出现失衡情况,属于 LL型
树5是在右子树左子节点出现失衡情况,属于 LR型
下面我就一一讲解怎么去通过旋转来处理上面这四种失衡问题。
2.2 LL型失衡(右旋)
右旋就是通过顺时针旋转来实现平衡,就是处于右边的根结点落下变为叶子节点,而中间的节点变为新的根节点,最左边的叶子节点保持不变,最后形成新的结构。
动图演示:
2.3 RR型失衡(左旋)
对于RR型失衡,通过逆时针旋转,最左边的根结点落下,变成叶子节点,中间的节点变为新的根节点,最右边的叶子节点保持不变。
动图演示:
2.4 LR型失衡(先左旋再右旋)
对于LR型失衡,先在根节点左子树进行左旋处理,变成LL型失衡,然后再去通过右旋来实现整体平衡。动图演示如下:
先左旋:
再右旋:
2.5 RL型失衡(先右旋再左旋)
对于RL型失衡,在根节点的右子树先进行右旋处理,形成RR型失衡,然后整体进行左旋处理,最后达到平衡状态,动态演示如下:
先右旋:
再左旋:
2.6构建平衡二叉树代码实现
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int Datatype;
typedef struct AVLtree {
Datatype data;
int heigh;//高度
struct AVLtree* left;
struct AVLtree* right;
}Node;
//绝对值
int abs(int a, int b) {
return a - b > 0 ? a - b : b - a;
}
//创建一个节点
Node* Create_node(Datatype data) {
Node* new_node = (Node*)malloc(sizeof(Node));
if (!new_node) {
printf("ERROR\n");
exit(-1);
}
new_node->data = data;
new_node->left = new_node->right = NULL;
new_node->heigh = 0;
return new_node;
}
//获取高度
int get_height(Node* T) {
if (!T)
return 0;
else
return T->heigh;
}
//1.插入右子树右节点,右旋
void right_rigth(Node** root) {
Node* k = (*root)->right;
(*root)->right = k->left;
k->left = (*root);
k->heigh = get_height(k->left) > get_height(k->right) ? get_height(k->left) : get_height(k->right);
(*root)->heigh = get_height((*root)->left) > get_height((*root)->right) ? get_height((*root)->left) : get_height((*root)->right);
*root = k;
}
//2.插入左子树左节点,失去平衡,左旋
void left_left(Node** root) {
Node* k = (*root)->left;
(*root)->left = k->right;
k->right = (*root);
k->heigh = get_height(k->left) > get_height(k->right) ? get_height(k->left) : get_height(k->right);
(*root)->heigh = get_height((*root)->left) > get_height((*root)->right) ? get_height((*root)->left) : get_height((*root)->right);
*root = k;
}
//3.插入左子树,右节点,失去平衡,先右旋再左旋
void left_right(Node** root) {
right_rigth(&((*root)->left));
left_left(&(*root));
}
//4.插入右子树左节点,失去平衡,先左旋再右旋
void rigth_left(Node** root) {
left_left(&((*root)->right));
right_rigth(&(*root));
}
//插入节点
void Insert_node(Datatype data, Node** root) {
if (!(*root)) {
(*root) = Create_node(data);
return;
}
else {
if (data < (*root)->data) {
Insert_node(data, &((*root)->left));
if (get_height((*root)->left) - get_height((*root)->right) > 1) {//如果出现失衡的情况
if(data<(*root)->left->data)
left_left(&(*root));
else
left_right(&(*root));
}
}
else if (data > (*root)->data) {
Insert_node(data, &((*root)->right));
if (get_height((*root)->right) - get_height((*root)->left) > 1) {//如果出现失衡的情况
if(data>(*root)->right->data)
right_rigth(&(*root));
else
rigth_left(&(*root));
}
}
else {
printf("Do not insert numbers of the same size!\n");
return;
}
}
(*root)->heigh++;
}
//创建平衡二叉树
void Create_AVLtree(Node** T, Datatype* data,int n) {
if (!data) {
return;
}
for (int i = 0; i < n; i++) {
Insert_node(data[i],T);
}
}
3.删除节点操作
在对平衡二叉树进行删除节点的操作时候,跟插入节点也是一样的,会出现失衡的情况,那这里就要通过旋转来去处理了,要进行那种旋转就要根据删除的节点属性去看,以下有4种情况:
- 删除叶子结点
- 删除结点只有左子树
- 删除结点只有右子树
- 删除结点有左、右子树
下面就一个一个来看,怎么去进行删除吧
3.1删除叶子节点
删除叶子节点后要看,平衡二叉树是否失衡,如果没有失衡就直接删除即可;如果出现了失衡的情况就要从根节点开始向上依次检索看从哪个位置开始失衡了,一经发现失衡就进行相对于的旋转处理,直到根结点为止。
3.2删除只有一个左(右)子树的节点
这种情况就要用这个被删除节点的相邻节点来去顶替这个节点,然后整体进行相对于的旋转操作。如下图所示:
3.3删除有左右子树的节点
要删除的节点有左右子树的话,那么就找到左子树的最大节点或者找到右子树最小节点来替换掉这个节点,然后把这个节点的空间释放掉就行了。
3.4删除节点代码实现
//查找数据节点
Node* Search_node(Node *T,Datatype target) {
if (!T) {
return NULL;
}
if (target == T->data)
return T;
else if (target < T->data)
Search_node(T->left, target);
else
Search_node(T->right, target);
}
//删除指定数据的节点
void Del_node(Node** T, Datatype target) {
Node* dnode = Search_node(*T, target);
if (!dnode){//如果查找不成功,就返回
printf("bye~~~error\n");
return;
}
if (target < (*T)->data) {
Del_node(&(*T)->left, target);
if (get_height((*T)->left) - get_height((*T)->right) > 1) {
if (target < (*T)->left->data)
left_left(&*T);
else
left_right(&*T);
}
}
else if (target > (*T)->data) {
Del_node(&(*T)->right, target);
if (get_height((*T)->right) - get_height((*T)->left) > 1) {
if (target < (*T)->right->data)
rigth_left(&*T);
else
right_rigth(&*T);
}
}
//此时(*T)就是要删除的节点
//1.如果这个节点都有左右子树的话
else if ((*T)->left && (*T)->right) {
//找到右子树最左边的节点,也就是比这个要删除节点大一位的节点
Node* min = (*T)->right;
while (min->left)
min = min->left;
(*T)->data = min->data;
Del_node(&(*T)->right, min->data);
}
//2.如果这个节点有一个左右子树或者没有子树的情况
else {
Node* dnode = (*T);
(*T) = (*T)->left ? (*T)->left : (*T)->right;//把这个节点进行替换掉
free(dnode);//释放空间,删除这个节点
}
//高度重新处理
if((*T))
(*T)->heigh=get_height((*T)->left)>get_height((*T)->right)?
get_height((*T)->left)+1: get_height((*T)->right)+1;
}
4.平衡二叉树相关操作
4.1查找数据
查找数据然后返回这个节点,我们直接通过二分查找就行了,代码如下:
//查找数据节点
Node* Search_node(Node *T,Datatype target) {
if (!T) {
return NULL;
}
if (target == T->data)
return T;
else if (target < T->data)
Search_node(T->left, target);
else
Search_node(T->right, target);
}
4.2判断是否为平衡二叉树
根据平衡二叉树的特性,我们需要判断这个二叉树是否排序,是否满足左右子树高度差不大于1 ,这里就需要用到递归一层一层检索了,最后取和运算,代码如下:
//判断一个树是否为平衡二叉树
bool Judge(Node* T) {
if (!T){
printf("NULL\n");
exit(0);
}
if(T->left->data>T->data||T->right->data<T->data)
return false;
if (abs(get_height(T->left) - get_height(T->right)) > 1)
return false;
else
return true;
return Judge(T->left)&&Judge(T->right);
}
4.3中序遍历输出
//中序遍历输出
void In_travel(Node* T) {
if (!T)
return;
In_travel(T->left);
printf("%d ", T->data);
In_travel(T->right);
}
4.4销毁平衡二叉树
销毁平衡二叉树就要把每一个节点的空间释放掉,那就直接从下往上去释放空间,代码如下:
//销毁
void Destroy(Node** T) {
if (!*T)
return;
Destroy(&(*T)->left);
Destroy(&(*T)->right);
free(*T);
}
以上就是本期的全部内容了,我们下一期再见!
分享一张壁纸: