c语言求平衡因子,平衡二叉树(AVL树)的基本操作

0x00、平衡二叉树的定义

平衡二叉树(AVL树)是一种特殊的二叉搜索树,只是在二叉搜索树上增加了对"平衡"的需求。

假如一棵二叉搜索树,按照“1,2,3,4,5”的顺序插入数据,会发现二叉树甚至变成了一个线性的链表状结构,这样查找数据的时间复杂度就会达到O(n),为了优化这一点,使二叉查找树时间复杂度保持O(log n)的级别,我们就需要调整树的插入方式,使之保持住树的结构。

这里引入一个定义:

平衡因子:二叉树的某个节点的左子树与右子树高度之差称为结点的平衡因子。

而平衡二叉树就是要保证平衡因子绝对值不能超过1。也就是左子树高度比右子树高度最多大1。

cb827b4d9a5950a09c0126a487e912b1.png

0x01、平衡二叉树的C语言实现

首先定义平衡二叉树,这和定义二叉搜索树有些不同,定义的结构体必须加上当前子树的高度。也就是这样:

struct node{

int data;    //数据域

int height;  //当前子树高度

node* lchild, *rchild;        //左右孩子

};

这样定义的好处是方便上一节点计算平衡因子。

计算平衡因子之前我们先定义计算高度的函数,直接返回root->height即可。如果root为NULL,说明是叶子节点,返回0即可

int getHeight(node* root){

return root == NULL ? 0 : root->height;

}

接着我们计算平衡因子,即左子树比右子树高了多少:

int getBalanceFactor(node* root){

return getHeight(root->lchild) - getHeight(root->rchild);

}

当我们执行了插入操作,二叉树发生改变的时候,我们需要重新计算高度。

某节点的高度值等于左右子树高度值最大的,加上1。(这是高度值的定义)

更新高度值:

void updateHeight(node* root){

root->height = max(getHeight(root->lchild), getHeight(root->rchild)) + 1;

}

0x02、平衡二叉树的查找

AVL树的查找与一般二叉搜索树的查找方法一样。代码如下:

void search(node* root, int data){

if(!root) return;    //查找失败

if(root->data == data)//找到了

printf("%d", root->data);

if(root->data > data) //大了,访问左节点

search(root->lchild, data);

else search(root->rchild, data);

}

0x03、平衡二叉树的插入操作

平衡二叉树的插入操作较为复杂,我们为了保持二叉树的平衡状态,就要在二叉树失衡的时候对二叉树进行旋转。

常见的旋转方式有LL型(单次右旋)、RR型(单次左旋)、LR型(先左旋,再右旋)、RL型(先右旋,再左旋)

下面我们进行详细介绍:

1、LL型(单次右旋)

假设一棵平衡二叉树再插入数据后,成了这个样子:

36d46cffdb06bdcfe84d051f28577bfe.png

图中的 4 和 12 的高度分别是 3 和 1 ,高度差为 2 ,此时,二叉树不平衡了。对于这种由根节点的左子树的左子树造成平衡因子为2的失衡,我们要使用LL型旋转,即单次右旋,使其平衡。

正确的旋转方式是:使原二叉树的 根节点(K2) 的 左孩子(k1) 做为 新根节点,新根节点(k1) 原来的 右子树(Y) ,做为 原来的根节点(K2) 的左子树

如果记不住的话可以按照这个思路来记忆:

既然左边高度比右边高,就让左子树的根节点称为新根,这样高度就减小了1,我们把左子树K1"揪起来",让原根节点k2"掉下去",这样K1就多了一个孩子,而X小于K1,K2大于K1,Y大于K1且Y小于K2,这样把Y"拽下来"插到K2的左边,仍然符合二叉搜索树的规则,而且还处理好了K1的关系,多么完美!

3b4b0164f23f9e5b539d943dcd1f7ad7.png

转转后,K1和K2(新根与原来的根)的高度发生了变化,而XYZ(三个子树)没有发生变化,所以我们只需要更新K1和K2的高度值即可。

用C语言实现,就是:

void LLrotation(node* &root){

node* temp = root->lchild;    //让temp=K1

root->lchild = temp->rchild;    //让root的左节点变为Y

temp->rchild = root;    //让新根K1的右节点指向K2

//以上,旋转完毕

updateHeight(temp);    //更新新根的节点高度

updateHeight(root);    //更新老根的节点高度

root = temp;    //把树的根换成我们定义的新根

}

2、RR型(单次左旋)

假设一棵二叉树插入数据后的状态:

0e5dbb5d6b38a68d73768c965b5829c0.png

很显然,失衡了。平衡因子为-2。对于这种根节点的右子树的右子树造成平衡因子为-2的失衡,我们采用RR旋转,即单次左旋。

单次左旋的思路就是单次右旋倒过来想,我们先将原根节点的右节点K2当做新根,将K2的左子树当做原根K1的右子树。

209811d0afbd5b788aac982fb563b259.png

同样的,只有K1和K2的高度发生了变化,所以我们只需要更新K1和K2的高度即可。

C语言实现:

void RRrotation(node* &root){

node* temp = root->rchild;    //另temp=K2

root->rchild = temp->lchild;    //旧根的右节点为新根的左节点

temp->lchild = root;    //新根的右节点等于旧根

//以上,旋转完毕

updateHeight(temp);    //更新节点高度

updateHeight(root);

root = temp;    //重赋值

}

3、LR型(先单次左旋,后单次右旋)

一棵二叉树,插入数据后形成了这个鬼样子

b7b0c71b210d895c40faa0d5cd85d183.png

4的高度为3; 而12的高度为1,平衡因子即高度差为2,这是由于根节点的左子树的右子树造成了平衡因子为2的失衡,通常采用LR型旋转,即先左旋,后右旋。

先左旋,也就是让K3的左孩子单次左旋,以K1为根节点,进行单次左旋,使得K2成为根节点,K1成为K2的左节点,K2原来的左节点B成为K1的右节点。这样就完成了单次左旋

接着对左旋后的二叉树,以K3为根节点,进行单次右旋,让K3的左节点K2成为新根,K2的右孩子C成为K3的左孩子,让K3做K2的右孩子。

71cbb9593df550e1f5f5a4112b61454c.png

这里K1、K2、K3的高度均发生了变化,ABCD的高度没有发生变化。

说起来很麻烦,其实实现起来很简单。因为我们有了前面LL旋转和RR旋转的基础,直接对子树操作即可。

C语言实现:

void LRrotation(node* &root){

RRrotation(root->lchild);    //先对根节点的左孩子单次左旋

LLrotation(root);

}

4、RL型(先单次右旋,后单次左旋)

二叉树数据插入后:

7576f4fe5a95732ae9a6c7436ac3db4b.png

此时的状态是:由于根节点的右节点的左子树造成了平衡因子为-2的失衡,我们通常采用RL型旋转,即先单次右旋,后单次左旋。

36a6f81b9fee5a267e1d856579970abc.png

其实就是把LR倒过来啦,不做详细解释了。(敲键盘敲的手指好痛,呜呜呜~~~~(>_

C语言实现:

void RLrotation(node* &root){

LLrotation(root->rchild);    //先对根节点的左孩子单次左旋

RRrotation(root);

}

写了这么多,总结一下吧。根据这个表格进行判断AVL树以何种旋转类型保持平衡:

树型

判定条件

调整方法

LL(单次右旋)

BF(root) = 2;BF(root->lchild) = 1

对root单次右旋

RR(单次左旋)

BF(root) =-2;BF(root->rchild) =-1

对root单次左旋

LR(先左后右)

BF(root) = 2;BF(root->lchild) =-1

对root->lchild进行左旋,再对root进行右旋

RL(先右后左)

BF(root) =-2;BF(root->rchild) = 1

对root->rchild进行右旋,再对root进行左旋

*BF代表平衡因子

0x04、二叉平衡树的插入

还记得二叉搜索树是怎样插入数据的吗?

void insert(node* &root, int data){

if(root == NULL){

root = new node;

root->data = data;

root->lchild = root->rchild = NULL;

}

if(root->data <= data)

insert(root->rchild, data);

else insert(root->lchild, data);

}

这段代码是不考虑平衡关系的插入代码。我们要做的是在这串代码上稍加修改,使之能及时通过旋转,保证平衡关系。

我们的思路是:沿用上面代码进行插入,插入后更新树的高度,接着判断平衡因子,根据平衡因子判断四种类型的旋转,最后执行旋转。

C语言实现:

void insert(node* &root, int data){

if(root == NULL){    //插入位置

root = new node;

root->data = data;

root->lchild = root->rchild = NULL;

root->height = 1;    //由于插入的时候是叶子节点,所以初始高度为1

return;

}

if(root->data > data){    //节点数据大,往左插入

insert(root->lchild, data);    //继续递归寻找插入位置

updataHeight(root);        //因为插入了一个节点,所以树根的高度会发生变化

if(getBalanceFactor(root) == 2)    //根节点平衡因子为2

if(getBalanceFactor(root->lchild) == 1) //根节点左孩子平衡因子为1,根据表格,采用LL型

LLrotation(root);

else if(getBalanceFactor(root->lchild) == -1)   //根节点左孩子平衡因子为-1,LR型

LRrotation(root);

}

else{    //节点数据小,往右插入

insert(root->rchild, data);

updataHeight(root);

if(getBalanceFactor(root) == -2){

if(getBalanceFactor(root->rchild) == -1) //RR型

RRrotation(root)

else if(getBalanceFactor(root->rchild) == 1)//RL型

RLrotation(root);

}

}

}

这里往左插入后,只需要判断平衡因子是否为2,是因为如果树之前是平衡的,往左插入的时候,不可能造成平衡因子为-2,只有往右插入的时候才会是-2,所以判断平衡因子 是否为-2就多余了。

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

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

相关文章

学校老师绝对不会教的方法,让你的孩子拥有一个开挂般的人生!

比勤奋更重要的&#xff0c;是孩子的思维能力。从上幼儿园开始&#xff0c;很多父母很喜欢给孩子报各种兴趣班&#xff0c;比如钢琴班、英语班、乐高班、报各种各样的课程&#xff0c;就是希望孩子具有18般武艺&#xff0c;赢在起跑线上。其实除了外在的能力&#xff0c;不显山…

解答网友提问:如何构建动态表达式实现高级查询服务

上次我们介绍了"一秒创建高级查询服务"。前天&#xff0c;有网友在公众号后台问我&#xff0c;怎么使用动态表达式&#xff1a;我想应该是客户提出了更高的要求&#xff0c;查询的条件不仅限于大于、小于&#xff0c;更加多样化&#xff0c;需要动态组合成条件&#…

π!到底蕴藏了多少不为人知的秘密?|今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源网络&#xff0c;侵权删&#xff09;赶紧检查一下π里面有没有你的秘密↓ ↓ ↓

.net core 下的分布式事务锁

系统分布式锁的用法公司框架新增功能分布式锁&#xff1a;锁的性能之王&#xff1a;缓存 > Zookeeper > 数据库锁的实现实现原理&#xff1a;核心采用StackExchange.Redis的LockTake方法实现。支持同步获取锁&#xff0c;或者等待直到超时获取锁。/// <summary>///…

刚刚!华为mate30 pro全球首发,三星黯然失色,iPhone11甚至都被吓降价了

全世界只有3.14 % 的人关注了青少年数学之旅众望所归&#xff0c;9月19日&#xff0c;华为在德国慕尼黑发布了Mate30系列。看完发布会&#xff0c;数据汪给大家总结了几个看点&#xff1a;1.全球一样的版本本次华为mate30系列采用的是EMUI 10系统&#xff0c;不会搭载谷歌旗下的…

c语言埃尔米特插值思路,【数学建模算法】(26)插值和拟合:埃尔米特(Hermite)插值和样条插值...

1.埃尔米特(Hermite)插值1.1.Hermite插值多项式如果对插值函数&#xff0c;不仅要求它在节点处与函数同值&#xff0c;而且要求它与函数有相同的一阶、二阶甚至更高阶的导数值&#xff0c;这就是 Hermite 插值问题。本节主要讨论在节点处插值函数与函数的值及一阶导数值均相等的…

剖析XAML语言

这节剖析一下XAML(读作&#xff1a;zaml)——这一WPF中的UI设计语言。XAML在wpf中&#xff0c;UI部分使用xaml语言来编写&#xff0c;xaml语言是由xml语言派生而来的语言&#xff0c;所以在xaml中我们可以看到很多熟悉的特点&#xff1a;它也是使用标签构建页面&#xff0c;一个…

中国最神秘的一所大学,它只存在过8年,却成了永远的第一

全世界只有3.14 % 的人关注了青少年数学之旅本文授权转载于公众号&#xff1a;物道精致生活&#xff08;wudaojieqi&#xff09;&#xff0c;转载请联系物道中国曾经有过这么一所大学&#xff1a;在抗日战争中仓促搭起&#xff0c;被称为“史上最穷”&#xff0c;校舍破旧得梁思…

ibatise 没有大于等于吗_库里+杜兰特并没有大于等于2!或许他和库里搭配将更强...

NBA历史风卷云涌&#xff0c;巨星层出不穷&#xff0c;就算是称霸一时的巨星组合亦是多不胜数。默契的魔术师与贾巴尔&#xff0c;强悍的斯托克顿与马龙&#xff0c;绝对统治的乔丹与皮蓬&#xff0c;飞天遁地的科比与奥尼尔。就算是近十年&#xff0c;有如韦德与詹姆斯&#x…

一句话征服了美国人,这位饱受争议的数学博士竟从未上过学?

全世界只有3.14 % 的人关注了青少年数学之旅前两天&#xff0c;有位不愿意透露姓名的模友问了超模君一个问题&#xff1a;虽然这个问题超模君已经解答过无数遍了&#xff0c;但看到模友如此虔诚的态度&#xff0c;超模君决定今天再给模友们讲一个犹太小伙用数学征服美国军官的故…

使用 C# 开发浏览器扩展

使用 C# Blazor 开发浏览器扩展Intro前段时间听了 Justin 大佬分享的 Blazor 开发浏览器扩展&#xff0c;觉得很不错&#xff0c;C# 可以做更多有趣的事情了&#xff0c;很多需要在服务器端做的事情可能就可以在客户端里实现了&#xff0c;而且高度可以复用已有的 C# 代码&…

一个设置ip的vbs脚本

经常在两个网段间转换 常改ip&#xff0c;找了一个改ip的脚本稍微改了一下&#xff0c;让他适合我的情况&#xff08;自动判断我的ip&#xff09;strComputer "."SetobjWMIService GetObject("winmgmts:\\"&strComputer &"\root\cimv2")…

心动的本质是什么_《心动的信号3》:在“烟火气”里嗑糖,素人恋爱究竟有多上头?...

文 | 土豆2018年&#xff0c;一档画风清新&#xff0c;以素人恋爱为主体、辅之以明星观察为核心的恋爱社交真人秀节目&#xff0c;走红于市场。彼时国内综艺市场&#xff0c;尚且还处于竞技类真人秀、偶像综艺的爆发期——《心动的信号》播出以后&#xff0c;不仅成功开启了国内…

android 如何动态设置margin,Android 动态设置margin

android的view中有setPadding&#xff0c;但是没有直接的setMargin方法。如果要在代码中设置该怎么做呢&#xff1f;可以通过设置view里面的LayoutParams设置&#xff0c;而这个LayoutParams是根据该view在不同的GroupView而不同的。布局文件如下:xmlns:tools"http://sche…

老是担心数学学不好?是因为你的数学老师不是爱因斯坦!

各位模友&#xff0c;大家好我是小木相信上学的时候&#xff0c;数学对于很多人来说&#xff0c;无疑是个坑&#xff01;好不容易毕业了&#xff0c;好奇又好学的小表妹每次都能完美地引起小木的心酸历程。就在小木一边回忆起自己的心酸历程的同时&#xff0c;不禁感叹&#xf…

oracle 产看执行计划_ODBA 技能SPM计划

OBA技能1-获取执行计划OBA技能2-执行计划顺序OBA技能&#xff13;-执行计划顺序表连接ODBA 技能&#xff14;实战执行计划ODBA 技能5 固定执行计划因为每次统计信息作业在收集完信息后&#xff0c;会触发ACS自适应游标管理程序&#xff0c;进行对绑定变量的窥探工作&#xff0c…

android动画设置的单位,Kotlin语言入门—实现单位转换,view设置,动画等

dp转换为px在android开发中&#xff0c;dp sp px之间的转换是不可避免的&#xff0c;在使用java语言开发时&#xff0c;往往会做个工具类进项转化。这样的工具类在网上很多&#xff0c;这里就不在展示了。如果使用Kotlin语言开发&#xff0c;则可以通过通过Extension来优雅的解…

微软面向初学者的机器学习课程:1.1-机器学习介绍

写在前面&#xff1a;最近在参与microsoft/ML-For-Beginners的翻译活动&#xff0c;欢迎有兴趣的朋友加入&#xff08;https://github.com/microsoft/ML-For-Beginners/issues/71&#xff09;机器学习介绍![机器学习&#xff0c;人工智能&#xff0c;深度学习-有什么区别?](ht…

遭央视曝光的“AI算命”,背后竟然隐藏了一个价值千亿的市场!?

全世界只有3.14 % 的人关注了青少年数学之旅还记得儿时算命先生曾对我说&#xff1a;等你25岁那年&#xff0c;会黄袍加身&#xff0c;每天与大鱼大肉为伍。如今眼看着25逐步逼近&#xff0c;数据汪看到美团的外卖小哥都有种莫名的“亲切感”。爆红的“AI算命”言归正传&#x…

微软面向初学者的机器学习课程:1.2-机器学习的历史

写在前面&#xff1a;最近在参与microsoft/ML-For-Beginners的翻译活动&#xff0c;欢迎有兴趣的朋友加入&#xff08;https://github.com/microsoft/ML-For-Beginners/issues/71&#xff09;机器学习的历史作者Tomomi Imura[1]课前测验[2]在本课中&#xff0c;我们将走过机器学…