通宵爆肝:C语言下的平衡二叉树(Avl)原来如此简单!

文章目录

  • 平衡二叉树的构造过程
    • 1 算法描述
  • 平衡二叉树的编程
    • 1 树上结点的高度计算
    • 2 LL调整函数
    • 3 RR调整函数
    • 4 LR调整函数
    • 5 RL调整函数
    • 6 根据结点的值、动态构造平衡二叉树


平衡二叉树的构造过程

对一个查找问题而言,查找表的存储结构、应该组织成二叉树结构。而把一个离散的数据、组织成最接近满二叉排序树的方法,最常见的就是平衡二叉树。

对平衡二叉树而言,有四种调整方式,就是LL、LR、RR、RL四种方式。

1 算法描述

(1) LL调整

对图1而言,如插入10,就是下面的过程:
在这里插入图片描述
插入的结果就是:如40为根,则左子树高、减去右子树、高度差是2。在这种情况下,需要调整,调整的结果就是:注意K1、K2结点的变化。

在这里插入图片描述
(2) RR调整

对下面的树而言,插入80则发生RR调整:
在这里插入图片描述
对上述二叉树、进行调整就是下图的过程:注意K1、K2结点的变化。

在这里插入图片描述
(3) LR调整

对以下的图,如果插入35,则要进行LR调整:

在这里插入图片描述
注意下面K3的变化:

在这里插入图片描述在这里插入图片描述.
(4) RL调整

在以下的树上,如果要插入53,则要进行的调整就是RL调整,注意过程:

在这里插入图片描述
此时要调整的过程、类似LR调整,过程如下:注意K1的变化。

在这里插入图片描述在这里插入图片描述

平衡二叉树的四种调整介绍完毕,你能就任意一组数据、完成构造平衡二叉树的过程么?

平衡二叉树的编程

1 树上结点的高度计算

从前面的平衡二叉树的构造过程可知:对平衡二叉树,最关键的问题是结点的高度计算问题,如果每个结点的高度有了,则左右子树的高度差也就可以计算了。

我们从二叉排序树的结构中补充一个字段:Height,同时修改结构名称为AvlNode,目的就是构造平衡二叉树。

struct AvlNode{int Element;struct AvlNode  *Left;struct AvlNode  *Right;int    Height;};

我们定义:当前结点的高度、是取左右孩子高度最大值再加1,所以对一下的结点,有:

在这里插入图片描述
注意这个高度编法,它是从树的叶结点开始的,这样的高度计算方法和从树根编下来有差异,但计算左右树高度差都是一样的。之所以这么编高度,是因为编程中确定叶结点高度为0非常简单。

struct AvlNode *Insert(int X, struct AvlNode *T)
{if(T==NULL){T =(struct AvlNode *)malloc(sizeof(struct AvlNode));if(T==NULL) {printf("内存不足\n");exit(0);}T->Element=X; T->Left=T->Right= NULL;T->Height=0;}if(X<T->Element) T->Left=Insert(X,T->Left);if(X>T->Element) T->Right=Insert(X,T->Right);T->Height=Max(Height(T->Left), Height(T->Right))+1;
return T;
}

在表1的第7行,我们新构造的每个结点高度都是0;

在表1的第11行,当前结点T的高度是T的左、右孩子结点高度最大者加1,这样构造的树、其每个结点的高度就如同表9。

能插入结点值为X并构造二叉排序树的程序见AvlTree0.c,它能给每个结点计算高度,当然,这个程序还不能构造平衡二叉树,它依然构造的是二叉排序树。但我们知道:平衡二叉树是二叉排序树的一种特殊情况。我们就要根据这个程序,不断修改出一个能构造平衡二叉树的程序。

2 LL调整函数

LL调整函数的过程见图1、图2,首先我们给这个树从叶结点开始编高度,其高度数据就是:

在这里插入图片描述
为测试方便,我们先编写main()函数、构造图1这样的二叉排序树,然后再编LL调整函数,所以main()就是:

main( )
{struct AvlNode *T;struct AvlNode BT[6];/*	构造图1的二叉排序树 */BT[0].Element=40;BT[0].Left=&BT[1];BT[0].Right=&BT[2];BT[0].Height=3;BT[1].Element=30;BT[1].Left=&BT[3];BT[1].Right=&BT[4];BT[1].Height=2;BT[2].Element=50;BT[2].Left=NULL;  BT[2].Right=NULL;BT[2].Height=0;  BT[3].Element=20;BT[3].Left=&BT[5];BT[3].Right=NULL;BT[3].Height=1;  BT[4].Element=35;BT[4].Left=NULL;  BT[4].Right=NULL;BT[4].Height=0;   BT[5].Element=10;BT[5].Left=NULL;  BT[5].Right=NULL;BT[5].Height=0;   T=LL(BT);jConvert(T);printf("\n");
}

有这个二叉树后再次回顾图2,所谓LL调整就是:

如树的根为K2,则:

K1为K2的左孩子;

K2的左孩子赋值为K1的右孩子;(35给40当左孩子)

K1的右孩子赋值为K2;(40是35的右孩子)

调整K2的高度;

调整K1的高度;

返回K1为根的树;

用C语言写出来就是:

struct AvlNode *LL(struct AvlNode *K2)
{
struct AvlNode *K1;
K1 = K2->Left;
K2->Left = K1->Right;
K1->Right = K2;
//注意各个结点高度计算
K2->Height = Max(Height(K2->Left), Height(K2->Right))+1;
K1->Height = Max( Height(K1->Left), K2->Height)+1;
return K1;
}

全部代码见LL.C,以下结果看看是否合理。

ID PID Value Height
0 -1 30 2
1 0 20 1
2 1 10 0
3 0 40 1
4 3 35 0
5 3 50 0

一定尝试着用遍历的结果来反推这个树的形态。

3 RR调整函数

RR调整和LL调整是对称操作,看着图4,所以是:

如K1是根结点;

K2是K1的右孩子;

把K2的左孩子赋值给K1当右孩子(53成为50的右孩子);

K1成为K2的左孩子(50成为60的左孩子);

重新计算K1的高度;

重新计算K2的高度;

返回K2为根的二叉树;

所以程序见下表:

struct AvlNode * RR(struct AvlNode *K1)
{
struct AvlNode *K2;
K2 = K1->Right;
K1->Right = K2->Left;
K2->Left = K1;K1->Height = Max(Height(K1->Left), Height(K1->Right))+1;
K2->Height = Max(Height(K2->Right), K1->Height)+1;
return K2;
}

对照表4,可知和LL调整是完全向对应、但方向相反。

下面是测试图4中RR调整的二叉树数据和main()程序

main( )
{struct AvlNode *T;struct AvlNode BT[6];/*  下面这组数据是测试RR平衡的 */BT[0].Element=50;BT[0].Left=&BT[1];BT[0].Right=&BT[2];BT[0].Height=3;   BT[1].Element=40;BT[1].Left=NULL;  BT[1].Right=NULL;  BT[1].Height=0;   BT[2].Element=60;BT[2].Left=&BT[3];BT[2].Right=&BT[4];BT[2].Height=2;   BT[3].Element=53;BT[3].Left=NULL;  BT[3].Right=NULL;  BT[3].Height=0;   BT[4].Element=70;BT[4].Left=NULL;  BT[4].Right=&BT[5];BT[4].Height=1;   BT[5].Element=80;BT[5].Left=NULL;  BT[5].Right=NULL;BT[5].Height=0;   T=RR(BT);jConvert(T);printf("\n");
}

这个程序的结果见下表:

在这里插入图片描述
仔细分析以下这些结点的关系,尝试着用遍历的方法反推这个树的形态。

4 LR调整函数

从图6的过程可以看到:

所谓LR调整、如K3为根的话,则K3的左孩子先进行RR调整,然后,整个树再以K3为根做LL调整。
完成这样的调整、写成程序会非常简单,就是:

struct AvlNode *LR(struct AvlNode *K3)
{K3->Left = RR(K3->Left);return  LL(K3);
}

每个调整过程都会自己调整结点高度,所以整个LR调整中不需要再次调整结点高度。

5 RL调整函数

从图8的过程可知:

如树的根结点是K1,则首先K1的右孩子进行LL调整,调整后的结果,再按K1为根进行RR调整。
所以整个RL的调整编程就是:

struct AvlNode *RL(struct AvlNode *K1)
{
K1->Right=LL(K1->Right);
return RR(K1);
}

LR.C、LR.C两个函数的具体执行情况见相关程序。各个程序的结果请同学们自己分析。这样,我们就完成了平衡二叉树的结点高度定义、以及四种调整方法的函数,有了这些基础,我们再次修改Insert()函数就有基础了。

6 根据结点的值、动态构造平衡二叉树

回顾表1,这个函数仅仅能构造二叉排序树,经过修改,目前能对插入的结点计算出高度来,我们要不断修改这个函数,让它能构造平衡二叉树。

首先,当插入X后,要构造出二叉排序树,其次要立刻判断这个结点的高度差,在表1中,在当前结点T上插入结点(以插入左子树左孩子为例)后,就是:

if(X<T->Element) T->Left=Insert(X,T->Left);

但现在,首先要计算T的左右孩子高度差,如果高度差是2,则再次判断X是否小于T的左孩子,如是,则必然为LL调整,否则为LR调整,就是:

if(X<T->Element)
{T->Left = Insert(X,T->Left);if(Height(T->Left)-Height(T->Right)==2)if(X<T->Left->Element)T=LL(T);elseT=LR(T);
}

注意上面的过程,是在X插入到当前结点T的左孩子的情况,此时,还有两种可能,在第5行的判断就是:插入到T的左孩子左子树,则做LL调整,如是在左孩子右子树,则做LR调整。同理可以编写出插入到右子树的情况,整个平衡二叉树的构造函数就是:

struct AvlNode *Insert(int X, struct AvlNode *T)
{
if(T==NULL){T =(struct AvlNode *)malloc(sizeof(struct AvlNode));if(T==NULL) {printf("内存不够,程序退出" );exit (0);}T->Element=X; T->Height=0;T->Left=T->Right= NULL;}if(X<T->Element){T->Left = Insert(X,T->Left);if(Height(T->Left)-Height(T->Right)==2)if(X<T->Left->Element)T=LL(T);elseT=LR(T);}if(X>T->Element){T->Right = Insert(X,T->Right);if(Height(T->Right)-Height(T->Left)== 2)if(X>T->Right->Element )T=RR(T);elseT=RL(T);}
T->Height=Max(Height(T->Left), Height(T->Right))+1;
return (T);
}

有这个函数后,我们可以编写个main()函数,测试这个函数是否正确,整个程序见AvlTree.c,这个程序用遍历的方法给出树的形态,注意自己再反推回去。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/285010.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[转]定了!2020年,6种将死的编程语言!

随着年度复工大戏的开播&#xff0c;编程界语言排行榜又要面临一次全新的洗牌&#xff0c;六大编程语言将要黄了&#xff01;此消息一出&#xff0c;令众多程序员心碎&#xff01;那么这将“亡”的六大语言中有你所擅长的吗&#xff1f; Perl 曾几何时&#xff0c;几乎每个人都…

正式发布丨AKS上的Dapr、ML、Gitops扩展

点击上方蓝字关注我们&#xff08;本文阅读时间&#xff1a;5分钟)我们很高兴地宣布在 Azure Kubernetes  Service&#xff08;以下简称AKS&#xff09;上启用的 Dapr、Azure 机器学习和 GitOps 三项新功能正式发布&#xff0c;可以通过称为“集群扩展”的功能在 AKS 集群上启…

C语言试题182之统计一串字符包含the的个数

📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款刷算法、笔试、面经、拿大公司offer神器👉 点击跳转进入网站 ✅作者简介:大家好,我是码莎拉蒂,CSDN博客专家(全站排名Top 50),阿里云博客专家、51CTO博客专家、华为云享专家 1、题目 题目: 统计一…

【ArcGIS Pro微课1000例】0013:NOAA全球1km分辨率DEM下载及拼接教程(附已拼接成果下载地址)

文章目录 一、全球1km分辨率DEM拼接成果介绍二、全球1km分辨率DEM原始数据下载三、全球1km分辨率DEM处理拼接流程四、全球1km分辨率DEM下载地址一、全球1km分辨率DEM拼接成果介绍 在ArcGIS Pro中加载拼接好的全球1km分辨率DEM数据集,如下图所示: 三维显示: 栅格源信息如下:…

国际主流产品信息管理规范SMBIOS支持LoongArch架构

SMBIOS支持龙架构&#xff08;LoongArch™&#xff09;龙芯生态标准统一近日&#xff0c;DMTF&#xff08;分布式管理任务组&#xff09;宣布SMBIOS规范支持龙架构&#xff08;LoongArch™&#xff09;&#xff0c;自此基于龙架构平台开发的基础硬件信息都将规范统一显示&#…

Git 常用命令(二)

用 git init 在目录中创建新的 Git 仓库。 $ mkdir test $ cd test/ $ git init Initialized empty Git repository in /Users/chenm/www/test/.git/ # 在 /www/test/.git/ 目录初始化空 Git 仓库完毕。 可以看到在你的项目中生成了 .git 这个子目录(隐藏文件)。 这就是你的 Gi…

【ArcGIS Pro微课1000例】0014:两种坐标系全国1km分辨率DEM下载地址(WGS84+Albers投影)

本文提供两种坐标系全国1km分辨率DEM下载地址(WGS84+Albers投影)。 文章目录 全国1km分辨率DEM数据预览WGS84地理坐标系Albers投影坐标系全国1km分辨率DEM数据下载全国1km分辨率DEM数据预览 WGS84地理坐标系 三维显示: 栅格信息:

AsyncTask的使用半解--!

AsyncTask,即异步任务,是Android给我们提供的一个处理异步任务的类.通过此类,可以实现UI线程和后台线程进行通讯,后台线程执行异步任务,并把结果返回给UI线程. .为什么需要使用异步任务? 我们知道,Android中只有UI线程,也就是主线程才能进行对UI的更新操作,而其他线程是不能直…

Andorid与webView交互,获取webView选中文字,兼容了iframe

js调试效果&#xff1a; 下面主要是拼装js代码 &#xff1a; /** * Description 获取webView选中文字内容 * param webView* param callBack*/public static void webViewGetSelectedData(WebView webView,webViewGetSelectedDataCallBack callBack) {String js "function…

C语言试题183之编写一个程序,从标准的输入读取一些字符,并统计下各类字符所占的百分比

📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款刷算法、笔试、面经、拿大公司offer神器👉 点击跳转进入网站 ✅作者简介:大家好,我是码莎拉蒂,CSDN博客专家(全站排名Top 50),阿里云博客专家、51CTO博客专家、华为云享专家 1、题目 题目: 编写一…

【ArcGIS Pro微课1000例】0015:ArcGIS Pro中属性字段分式标注案例教程

文章目录 1. 符号化2. 属性字段分式标注在ArcGIS及Pro中很容易实现格式化标签的,本文讲解在ArcGIS Pro中实现属性字段分式标注,结果如下图所示: 1. 符号化 右键数据图层→符号系统,打开符号系统对话框,住符号系统选择【唯一值】,字段1选择NAME。 2. 属性字段分式标注 加…

C语言试题184之编写一个函数,从标准输入读取一个字符串,把字符串复制到动态内存分配的内存中,并返回该字符串的拷贝,这个函数不应该对读入字符串的长度作任何限制

📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款刷算法、笔试、面经、拿大公司offer神器👉 点击跳转进入网站 ✅作者简介:大家好,我是码莎拉蒂,CSDN博客专家(全站排名Top 50),阿里云博客专家、51CTO博客专家、华为云享专家 1、题目 题目: 编写一…

[转]Linux面试题(2020最新版)

文章目录 Linux 概述 什么是LinuxUnix和Linux有什么区别&#xff1f;什么是 Linux 内核&#xff1f;Linux的基本组件是什么&#xff1f;Linux 的体系结构BASH和DOS之间的基本区别是什么&#xff1f;Linux 开机启动过程&#xff1f;Linux系统缺省的运行级别&#xff1f;Linux 使…

MSBuild 命令的简单使用

MSBuild 命令的简单使用独立观察员 2022 年 7 月 7 日位置在 VS 安装目录下&#xff0c;如&#xff1a;D:Microsoft Visual Studio2022EnterpriseMSBuildCurrentBin命令MSBuild 命令行参考 - MSBuild | Microsoft Docs&#xff08;https://docs.microsoft.com/zh-cn/visualstud…

ArcGIS实验教程——实验四十:ArcGIS洪水淹没分析案例教程

文章目录 一、洪水淹没效果动画演示二、实验数据三、实验过程1. 加载数据2. 符号化3. 夸大处理4. 动画制作5. 动画播放6. 导出动画基于数字高程模型 ( DEM )格网模型,实现给定水深情况下洪水淹没区的计算模型,讨论洪水淹没演进过程可视化实现的关键技术,以三维可视化方式,动…

C语言试题185之编写calloc函数,函数内部使用malloc函数来获取内存

📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款刷算法、笔试、面经、拿大公司offer神器👉 点击跳转进入网站 ✅作者简介:大家好,我是码莎拉蒂,CSDN博客专家(全站排名Top 50),阿里云博客专家、51CTO博客专家、华为云享专家 1、题目 题目: 编写ca…

【ArcGIS遇上Python】ArcGIS Python按照指定字段批量筛选不同类型的图斑(以土地利用数据为例)

基于土地利用数据,根据用地类型名称,批量筛选出不同类型的用地,生成不同类型的shp数据,以类型名称命名。 文章目录 1. 土地利用原始数据2. 根据名称批量筛选结果3. ArcGIS Python批处理代码ArcGIS Python根据字段属性批量筛选生成shp图层 1. 土地利用原始数据 2. 根据名称批…

【转载】【面试题】你是一个测试工程师,如何保证软件质量?

2019独角兽企业重金招聘Python工程师标准>>> *参*答*案&#xff1a;质量是有层次&#xff08;内部质量&#xff0c;外部质量&#xff0c;使用质量&#xff0c;过程质量&#xff09; 内部质量&#xff1a;主要指代码的质量&#xff0c;就需要引入开发工程师进…

C语言试题186之读取范围在1和标准输入读取的size之前每个数据出现的次数

📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款刷算法、笔试、面经、拿大公司offer神器👉 点击跳转进入网站 ✅作者简介:大家好,我是码莎拉蒂,CSDN博客专家(全站排名Top 50),阿里云博客专家、51CTO博客专家、华为云享专家 1、题目 题目: 读取范…

[转]常见的用户密码加密方式以及破解方法

【作者】张辉&#xff0c;就职于携程技术中心信息安全部&#xff0c;负责安全产品的设计与研发。 作为互联网公司的信息安全从业人员经常要处理撞库扫号事件&#xff0c;产生撞库扫号的根本原因是一些企业发生了信息泄露事件&#xff0c;且这些泄露数据未加密或者加密方式比较弱…