没事儿就学习(1):AVL树插入

2023-11-10 22:59
文章标签 学习 avl 插入 没事儿

本文主要是介绍没事儿就学习(1):AVL树插入,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

       AVL树是最先发明的自平衡二叉树,这是个啥树呢,为啥要平衡呢?我们可以在分析其原理的时候慢慢用C++实现它一下。

       故名思意,自平衡二叉树是一种可以实现自我平衡的二叉树,分开看是自动化的、平衡的、二叉树。数据结构中的二叉树是一种特殊的树,因为其限定了树中的每个节点最多只能包含两个子节点,那么根据其特性就不难发现在二叉树的第n层,就最多仅有2^(n-1)个子节点。二叉树可以沿任意一侧不断延伸到任意的深度,导致了二叉树可以具有各种各样的形状,比如下面画的巨丑的一张图,左边是一棵满二叉树,而右边则是一棵普通二叉树。

 

       规整的二叉树和不规整的二叉树差别在什么地方呢?从直观上来说,一棵树越趋向于满二叉树,那么它的通过递归查找的次数可能越少,效率就越高,因此趋向于将数据向满二叉树排列。那么一个简单的排序树是什么样的呢?我们先随便说一串数字,比如{1,2,4,8,5,7,3,9,6},在构建树的时候,先把第一个元素作为根节点,然后后续插入时将比节点中数值大的作为其右子节点,否则为左子节点,具体的流程可以从下图的步骤中看出。

      按顺序构建的二叉树长的和北斗七星一样,9个数值构建出的深度竟然达到了7层之多,如果按满二叉树的方式构建,最多只需要4层就足够进行摆放。那么有没有什么方法可以将狂野生长的二叉树变成比较规矩的,每一层都有足够节点的二叉树呢?平衡的概念就此提出。我们很明显的看出上图中最后生成的树一点都不平衡,根节点左侧啥也没有,右侧延伸出一个北斗七星,只有在左右两侧节点深度和数量差不多的情况下树才能平衡啊,不然就是一边倒了,所以我们要给出一些限定条件来转换节点之间的位置和顺序。


       平衡有相对的平衡和绝对的平衡,自然界中没有绝对的平衡存在,所有的平衡都有一定的缓冲范围,也就是所谓的阈值,超过这个阈值的限定即为不平衡,需要通过一定的手段对环境进行再平衡。这个阈值在自平衡二叉树中体现在每个结点的左子树和右子树的深度相差不能大于1,否则即为不平衡的状态,否则需要通过一系列的手段,左旋右旋将树转换到稳定态。我们先拿一个最简单的例子{1,2,3}进行一下平衡性实验,如下图所示,首先生成的树如上方所示,1作为根节点,2是1的右子节点,3是2的右子节点,那么根节点1的左子树深度为0,右子树深度为2,相差2>1已经处于不平衡的状态。那么怎么样可以使其变成平衡态呢?很明显,将2作为根节点可以有效解决这一问题。那么怎么将2作为根节点呢?我们可以把这三个节点向左旋转,1作为2的左子节点,3作为2的右子节点,旋转完毕后根节点2左子树深度为1,右子树深度为1,实现了平衡。这样的操作我们就称之为左旋同样的如果是左侧子树的深度大于右侧子树的深度,就需要对其进行右旋

       所以我们可以先定义一下二叉树节点的数据结构,每个节点可以存储一个值,存储左孩子和右孩子,存储当前平衡的状态,因此定义如下。

//枚举:平衡状态
enum HeightType
{LEFTHEIGHT,//左深RIGHTHEIGHT,//右深EQUALHEIGHT//平衡
};class TreeNode {
public:int value;//存储值TreeNode *leftChild;//左枝TreeNode *rightChild;//右枝HeightType heightType;//平衡状态//构造函数TreeNode(int value) {this->value = value;this->leftChild = nullptr;this->rightChild = nullptr;this->heightType = EQUALHEIGHT;};
};

    然后定义一下左旋操作,1的左孩子连向2的右孩子,2的左孩子连向1,再把2作为根节点,于是乎函数可以这样写。

bool TurnLeft(TreeNode* &node) {TreeNode* tempNode = node->rightChild;node->rightChild = tempNode->leftChild;tempNode->leftChild = node;node = tempNode;return true;
}

    然后再定义一下右旋操作,与左旋类似。

bool TurnRight(TreeNode* &node) {TreeNode* tempNode = node->leftChild;node->leftChild = tempNode->rightChild;tempNode->rightChild = node;node = tempNode;return true;
}

     但是仅仅这样旋转就结束了吗,很显然不是的,当不平衡发生的时候根节点和其子节点的平衡性符号相反时,1的平衡性符号为RH,3的平衡性符号为LH,如果仅仅进行左旋操作的话,得到的结果为:3为根节点,1为3的左孩子,2为3的右孩子,很明显与要求不符。因此需要先将3和2先调换位置,将2作为1的右孩子,3作为2的右孩子,使1和2的平衡性因子均为RH,然后再进行左旋操作。那么我们可以将左旋和右旋操作的函数写的更加完善一点。


bool BigTurnLeft(TreeNode* &node) {TreeNode *nextNode = node->rightChild;//转换节点位置if (node->heightType != nextNode->heightType) {node->rightChild = nextNode->leftChild;node->rightChild->rightChild = nextNode;nextNode->leftChild = nullptr;}//左旋TurnLeft(node);return true;
}bool BigTurnRight(TreeNode* &node) {TreeNode *nextNode = node->rightChild;//转换节点位置if (node->heightType != nextNode->heightType) {node->leftChild = nextNode->rightChild;node->leftChild->leftChild = nextNode;nextNode->rightChild = nullptr;}//右旋TurnRight(node);return true;
}

       在定义完毕基本的左旋和右旋操作后,我们就可以写AVL树的插入函数了,不如我们先小看一下代码?(没错,你没的选,我就是想让你看)

bool InsertIntoATLTree(TreeNode* &node, int value, bool &isGrow) {//如果没有根节点,则创造一个根节点if (node == nullptr) {node = new TreeNode(value);}else {if (value == node->value) { //判断值是否相同,相同则无需插入return false;}if (value < node->value) { //若value值小于当前节点值if (node->leftChild == nullptr) {  //左枝是否为空?将value作为该节点的左节点:递归向下判断TreeNode *newNode = new TreeNode(value);//新建node,连在当前node左枝node->leftChild = newNode;switch (node->heightType)//修改当前节点稳定性{case RIGHTHEIGHT:node->heightType = EQUALHEIGHT;break;case EQUALHEIGHT:node->heightType = LEFTHEIGHT;isGrow = true;break;default:break;}return true;}else {if (InsertIntoATLTree(node->leftChild, value, isGrow)) {if (isGrow) {switch (node->heightType){case LEFTHEIGHT:BigTurnRight(node);node->heightType = EQUALHEIGHT;node->rightChild->heightType = EQUALHEIGHT;isGrow = false;break;case RIGHTHEIGHT:node->heightType = EQUALHEIGHT;break;case EQUALHEIGHT:node->heightType = LEFTHEIGHT;break;default:break;}}}else {return false;}}}else {if (node->rightChild == nullptr) {  //右枝是否为空?将value作为该节点的右节点:递归向下判断TreeNode *newNode = new TreeNode(value);//新建node,连在当前node右枝node->rightChild = newNode;switch (node->heightType)//修改当前节点稳定性{case LEFTHEIGHT:node->heightType = EQUALHEIGHT;break;case EQUALHEIGHT:node->heightType = RIGHTHEIGHT;isGrow = true;break;default:break;}return true;}else {if (InsertIntoATLTree(node->rightChild, value, isGrow)) {if (isGrow) {switch (node->heightType){case LEFTHEIGHT:node->heightType = EQUALHEIGHT;break;case RIGHTHEIGHT:BigTurnLeft(node);node->heightType = EQUALHEIGHT;node->leftChild->heightType = EQUALHEIGHT;isGrow = false;break;case EQUALHEIGHT:node->heightType = RIGHTHEIGHT;break;default:break;}}}else{return false;}}}}return true;
}
 

      树的插入算法是一个递归的过程,首先将需要插入的数值与根节点RN 中的数值RV 进行对比,若V<RV 则与其左孩子进行对比,若V>RV则于其右孩子进行对比,直至某个节点没有左孩子或右孩子为止。构建新节点作为新的叶子,判断该叶子的父节点平衡状态,若父节点的平衡状态与叶子节点的悬挂方向相反则表示树没有生长,否则树发生生长,回溯是判断各个父节点的平衡状态,若父节点处于不平衡状态则进行左旋或右旋操作,旋转完毕后再往前回溯的节点不再改变平衡状态。

      以一个小例子作为插入的结束C={23, 16, 47, 53, 62, 11, 17, 8 , 3};




这篇关于没事儿就学习(1):AVL树插入的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/385839

相关文章

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

使用Python在Excel中插入、修改、提取和删除超链接

《使用Python在Excel中插入、修改、提取和删除超链接》超链接是Excel中的常用功能,通过点击超链接可以快速跳转到外部网站、本地文件或工作表中的特定单元格,有效提升数据访问的效率和用户体验,这... 目录引言使用工具python在Excel中插入超链接Python修改Excel中的超链接Python

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识