C++的数据结构(十):AVL树

        AVL树是一种自平衡的二叉搜索树,得名于其发明者G.M. Adelson-Velsky和E.M. Landis。在AVL树中,任何节点的两个子树的高度最多相差1,这种性质确保了AVL树的查找、插入和删除操作的时间复杂度接近O(log n)。

        AVL树是一种二叉搜索树,它通过调整节点间的结构来保持树的平衡。平衡因子(Balance Factor)是AVL树中一个关键的概念,定义为节点的左子树高度与右子树高度的差。在AVL树中,任何节点的平衡因子的绝对值不得超过1。如下图所示。

         AVL树通过旋转操作来保持树的平衡,AVL树在节点插入或删除后,若导致树不平衡,则会通过四种旋转操作之一来重新平衡树。这四种旋转操作分别是:左旋、右旋、左右旋和右左旋。

        1. 左旋操作通常发生在右子树比左子树高太多的时候。具体操作是:将当前节点(我们称其为T)的右子节点(我们称其为R)提升为新的根节点,将T节点降为R的左子节点,R的左子节点则成为T的右子节点。示例代码如下。

struct TreeNode {int val;int height;TreeNode* left;TreeNode* right;TreeNode(int val) : val(val), height(1), left(nullptr), right(nullptr) {}
};TreeNode* leftRotate(TreeNode* x) {TreeNode* y = x->right;  // y是x的右子节点x->right = y->left;      // y的左子节点成为x的右子节点y->left = x;            // x成为y的左子节点// 更新高度x->height = 1 + max(height(x->left), height(x->right));y->height = 1 + max(height(y->left), height(y->right));return y;  // 新的根节点
}

        2. 右旋操作与左旋相反,发生在节点不平衡,且平衡因子小于-1,同时左子节点的平衡因子小于等于0时。我们将左子节点提升为当前节点的父节点,当前节点变为左子节点的右子节点,完成右旋。示例代码如下。

TreeNode* rightRotate(TreeNode* y) {TreeNode* x = y->left;  // x是y的左子节点y->left = x->right;     // x的右子节点成为y的左子节点x->right = y;          // y成为x的右子节点// 更新高度x->height = 1 + max(height(x->left), height(x->right));y->height = 1 + max(height(y->left), height(y->right));return x;  // 新的根节点
}

         3. 左右旋是左旋和右旋的组合操作,发生在节点不平衡,且平衡因子大于1,同时右子节点的平衡因子小于0时。首先,对右子节点进行左旋,然后对整个树以当前节点为根进行右旋。

        4. 右左旋也是左旋和右旋的组合操作,但顺序相反。它发生在节点不平衡,且平衡因子小于-1,同时左子节点的平衡因子大于0时。首先,对左子节点进行右旋,然后对整个树以当前节点为根进行左旋。 

        在实际应用中,当我们对AVL树进行插入或删除操作时,一旦导致树不平衡,我们就需要根据节点及其子节点的平衡因子,决定执行哪种旋转操作,来恢复树的平衡。通过正确应用这四种旋转操作,我们可以确保AVL树始终保持平衡状态,从而保持高效的性能。

        需要注意的是,旋转操作只是AVL树维护平衡的一种方式,实际的应用场景中还需要处理其他情况,比如如何高效地查找节点、如何正确地插入和删除节点等。但无论何种情况,保持树的平衡都是AVL树设计的核心所在。

        AVL树由于具有良好的平衡性,常被用于需要高效查找、插入和删除操作的场景中,如数据缓存、关联数组、文件系统等。它提供了一种高效的存储和访问数据的方式,特别是在数据量大、操作频繁的情况下,AVL树能够保持较好的性能。

        下面是一个简单的AVL树的C++实现,包含插入、删除和打印树的函数。代码如下。

#include <iostream>
#include <stack>
#include <algorithm> 
using namespace std;// 定义AVL树的节点结构体
struct AVLNode {int key;int height;AVLNode* left;AVLNode* right;AVLNode(int k) : key(k), height(1), left(nullptr), right(nullptr) {}
};// 查找树中的最小值节点
AVLNode* minValueNode(AVLNode* node) {AVLNode* current = node;// 循环直到左孩子是nullptrwhile (current->left != nullptr)current = current->left;return current;
}// 获取节点的高度
int getHeight(AVLNode* N) {if (N == nullptr)return 0;return N->height;
}// 获取平衡因子
int getBalance(AVLNode* N) {if (N == nullptr)return 0;return getHeight(N->left) - getHeight(N->right);
}// 更新节点高度
void updateHeight(AVLNode* N) {if (N != nullptr)N->height = 1 + max(getHeight(N->left), getHeight(N->right));
}// 右旋
AVLNode* rightRotate(AVLNode* y) {AVLNode* x = y->left;AVLNode* T2 = x->right;x->right = y;y->left = T2;updateHeight(y);updateHeight(x);return x;
}// 左旋
AVLNode* leftRotate(AVLNode* x) {AVLNode* y = x->right;AVLNode* T2 = y->left;y->left = x;x->right = T2;updateHeight(x);updateHeight(y);return y;
}// 获取插入新节点后的平衡AVL树
AVLNode* insert(AVLNode* node, int key) {if (node == nullptr)return (new AVLNode(key));if (key < node->key)node->left = insert(node->left, key);else if (key > node->key)node->right = insert(node->right, key);else // Duplicate keys not allowedreturn node;// 更新高度updateHeight(node);// 获取平衡因子int balance = getBalance(node);// 左左情况if (balance > 1 && key < node->left->key)return rightRotate(node);// 右右情况if (balance < -1 && key > node->right->key)return leftRotate(node);// 左右情况if (balance > 1 && key > node->left->key) {node->left = leftRotate(node->left);return rightRotate(node);}// 右左情况if (balance < -1 && key < node->right->key) {node->right = rightRotate(node->right);return leftRotate(node);}return node;
}// 从AVL树中删除一个节点
AVLNode* deleteNode(AVLNode* root, int key) {if (root == nullptr)return root;if (key < root->key)root->left = deleteNode(root->left, key);else if (key > root->key)root->right = deleteNode(root->right, key);else {// 节点有两个子节点if (root->left && root->right) {AVLNode* temp = minValueNode(root->right);root->key = temp->key;root->right = deleteNode(root->right, temp->key);}// 节点只有一个子节点或没有子节点else {AVLNode* temp = root->left ? root->left : root->right;if (temp == nullptr) {temp = root;root= nullptr;} else {*root = *temp;}free(temp);}}if (root == nullptr)return root;// 更新高度updateHeight(root);// 获取平衡因子int balance = getBalance(root);// 右右情况if (balance > 1 && getBalance(root->left) >= 0)return rightRotate(root);// 左左情况if (balance < -1 && getBalance(root->right) <= 0)return leftRotate(root);// 左右情况if (balance > 1 && getBalance(root->left) < 0) {root->left = leftRotate(root->left);return rightRotate(root);}// 右左情况if (balance < -1 && getBalance(root->right) > 0) {root->right = rightRotate(root->right);return leftRotate(root);}return root;
}// 打印AVL树(中序遍历)
void inorder(AVLNode* root) {if (root != nullptr) {inorder(root->left);cout << root->key << " ";inorder(root->right);}
}// 主函数测试
int main() {AVLNode* root = nullptr;// 插入节点root = insert(root, 10);insert(root, 20);insert(root, 30);insert(root, 40);insert(root, 50);insert(root, 25);// 打印AVL树cout << "所构建的AVL树的中序遍历是 \n";inorder(root);cout << endl;// 删除节点root = deleteNode(root, 25);// 再次打印AVL树cout << "删除后修改的AVL树的中序遍历为 \n";inorder(root);cout << endl;return 0;
}

        总的来说,AVL树是一种非常有用的数据结构,它通过自动平衡保证了高效的查找、插入和删除操作。掌握AVL树的原理和操作对于理解数据结构和算法的重要性以及提高编程技能都非常有帮助。

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

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

相关文章

MongoDB基础入门到深入(七)建模、调优

文章目录 系列文章索引十一、MongoDB开发规范十二、MongoDB调优1、三大导致MongoDB性能不佳的原因2、影响MongoDB性能的因素3、MongoDB性能监控工具&#xff08;1&#xff09;mongostat&#xff08;2&#xff09;mongotop&#xff08;3&#xff09;Profiler模块&#xff08;4&a…

K8S认证|CKA题库+答案| 16. 升级集群

16、升级集群 CKA v1.29.0模拟系统免费下载试用&#xff1a; 百度网盘&#xff1a;https://pan.baidu.com/s/1vVR_AK6MVK2Jrz0n0R2GoQ?pwdwbki 题目&#xff1a; 您必须在以下Cluster/Node上完成此考题&#xff1a; Cluster Ma…

CTF网络安全大赛简单web题目:eval

题目来源于&#xff1a;bugku 题目难度&#xff1a;简单 一道简单web的题目 题目源代码&#xff1a; <?phpinclude "flag.php";$a $_REQUEST[hello];eval( "var_dump($a);");show_source(__FILE__); ?> 这个PHP脚本有几个关键部分&#xff0c;但…

太阳诱电:顺应时代需求的新型电容器为何能在全球得到广泛应用(下)

随着汽车电动化和电子控制化的进展&#xff0c;车载计算机和电气部件也在逐渐向大功率化的方向发展。而构成这些车载设备电源电路的电子元器件也必须随之进行技术革新。太阳诱电集团携手全资子公司ELNA&#xff0c;开发并供应新型电容器“导电性高分子混合铝电解电容器”&#…

【热门话题】一文带你读懂公司是如何知道张三在脉脉上发了“一句话”的

按理说呢&#xff0c;A公司和脉脉属于不同的平台&#xff0c;而且脉脉上大家可以匿名发言&#xff0c;所以&#xff0c;即便我坐在你边上&#xff0c;我发了一句话上去&#xff0c;你也不知道是谁发的。但通过一些技术&#xff0c;我们却可以分析出&#xff0c;公司是如何知道张…

IOC控制反转

IOC IOC&#xff0c;全称为Inversion of Control(控制反转)&#xff0c;是一种设计原则&#xff0c;它反转了传统编程中的控制流程。在传统的编程模式中&#xff0c;组件之间的依赖关系是由组件自身在内部创建和维护的。而在控制反转模式中&#xff0c;这种依赖关系由外部容器(…

PS —— 制作证件照

PS —— 制作证件照 裁剪工具魔棒工具油漆桶工具扩展画布 老是看编程&#xff0c;会有些疲劳&#xff0c;这个专栏我会放一些其他的知识&#xff0c;我们今天利用PS制作证件照&#xff08;注意&#xff0c;这里一些ps的基础操作我不会很展开的去讲&#xff09;&#xff1a; 裁…

深入解析R语言的贝叶斯网络模型:构建、优化与预测;INLA下的贝叶斯回归;现代贝叶斯统计学方法;R语言混合效应(多水平/层次/嵌套)

目录 ①基于R语言的贝叶斯网络模型的实践应用 ②R语言贝叶斯方法在生态环境领域中的应用 ③基于R语言贝叶斯进阶:INLA下的贝叶斯回归、生存分析、随机游走、广义可加模型、极端数据的贝叶斯分析 ④基于R语言的现代贝叶斯统计学方法&#xff08;贝叶斯参数估计、贝叶斯回归、…

抽象工厂模式(AbstractFactoryPattern)

文章目录 1.抽象工厂模式定义2.UML类图3.抽象工厂模式具体实现工厂模式实现单一产品族抽象工厂实现多产品族产品类工厂类使用 4.抽象工厂模式优缺点 1.抽象工厂模式定义 提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无需指定它们具体的类。 工厂方法模式是单一产…

2024电工杯B题食谱评价与优化模型思路代码论文分析

2024年电工杯数学建模竞赛B题论文和代码已完成&#xff0c;代码为B题全部问题的代码&#xff0c;论文包括摘要、问题重述、问题分析、模型假设、符号说明、模型的建立和求解&#xff08;问题1模型的建立和求解、问题2模型的建立和求解、问题3模型的建立和求解&#xff09;、模型…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-17讲 定时器按键消抖

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

计算机网络安全控制技术

1.防火墙技术 防火墙技术是近年来维护网络安全最重要的手段&#xff0c;但是防火墙不是万能的&#xff0c;需要配合其他安全措施来协同 2.加密技术 目前加密技术主要有两大类&#xff1a;对称加密和非对称加密 3.用户识别技术 核心是识别网络者是否是属于系统的合法用户 …

【设计模式深度剖析】【1】【结构型】【代理模式】| 玩游戏打怪、升级为例加深理解

&#x1f448;️上一篇:创建型设计模式对比 | 下一篇:装饰器模式&#x1f449;️ 目 录 代理模式定义英文原话直译如何理解&#xff1f; 3个角色UML类图1. 抽象主题&#xff08;Subject&#xff09;角色2. 代理类&#xff1a;代理主题&#xff08;Proxy Subject&#xff0…

UE5 OnlineSubsystem Steam创建会话失败解决方法

连接上Steam但是创建会话失败 解决方法 在DefaultEngine.ini中加上bInitServerOnClienttrue,这个其实在官方文档里用注释给出了&#xff0c;直接取消注释就行 删除项目目录中的Saved、Internmediate、Binaries目录 右键你的项目.uproject选择Generate Visual Studio project f…

ASP.Net MVC在控制台添加视图时没有模型类并且不能添加视图

情况如下&#xff1a; 解决方法&#xff1a; 1.查看vs能否创建asp.net mvc项目&#xff0c;这种情况一般是更换了vs打开老项目 2.点击跳转至修改安装选项界面 3.选择安装项即可 如果以上都有&#xff1a; 看看你的视图文件是否存在在项目中 也不能点击添加&#xff0c;如果…

探索数值分析的奥秘:掌握NumPy与Pandas基础

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、NumPy&#xff1a;数值计算的效率提升器 二、Pandas&#xff1a;数据处理与分析的利器 …

Linux文本三剑客之sed

set的定义&#xff1a; sed是一种流编辑器&#xff0c;流编辑器会在编辑器处理数据之前基于预先提供的一组规则来编辑数据流。 sed编辑器可以根据命令来处理数据流中的数据&#xff0c;这些命令要么从命令行中输入&#xff0c;要么存储在一个命令文本文件中。 sed的执行过程&a…

Nextjs 动态路由获取参数

目录 一、创建page目录下的路由文件 二、通过useRouter来获取动态参数 先看官方文档&#xff1a;Routing: Dynamic Routes | Next.js 使用Nextjs13版本参考官方文档后实现仍有问题&#xff0c;网上的材料大多和官网一致&#xff0c;经过测试后&#xff0c;我找到了这个解决方…

【日记】今天好困(407 字)

正文 4T 硬盘降价了&#xff0c;好心动。虽然只降了 10 块钱……. 为什么硬盘这么贵啊&#xff01;哼。 柜面上杂事好多。虽然一天到晚见不到几个客户&#xff0c;但杂事就是很多。一个头两个大。也不知道从哪儿冒出来的这么多事。 芒果干到了&#xff01;还没去取&#xff0c;…

JAVASE之类和对象(1)

路虽远&#xff0c;行则将至&#xff1b;事虽难&#xff0c;做则必成。 主页&#xff1a;趋早——Step 专栏&#xff1a;JAVASE gitte&#xff1a;https://gitee.com/good-thg 引言&#xff1a; 这篇文章我们只介绍前半部分&#xff0c;下一篇文章会介绍剩下的部分。 目录 一、…