暑假提升(2)[平衡二叉树之一--AVL树]

我不去想未来是平坦还是泥泞,只要热爱生命一切,都在意料之中。——汪国真


AVLTree

  • 1、诞生原因
  • 2、什么是AVL树
  • 3、如何设计AVL树
    • 3、1、AVL树节点的定义
    • 3、2、AVL树的插入
    • 3、3、平衡因子那些事
      • 3、3、1、平衡因子-2/2下的简单情况
      • 3、3、2、平衡因子-2/2下的复杂情况
  • 4、总结

1、诞生原因

已经有了二叉树了,那为什么我们需要去使用平衡二叉树这种类型呢?
其实原因还是在于,由于特殊情况的存在,二叉树不能真正的做到对所有的数据都能够优化,有时候处理的结果还不如不处理的结果,就例如在这篇文章中的所介绍的二叉树一样,其中的缺点也是显而易见的(直接点可以看到之前的文章)。
由于二叉树的本身缺陷,如果树中的元素接近有序或者是有序,都会造成二叉搜索树的大大退化,进一步可能成为单支树,时间复杂度退化成O(N)。
所以为了满足这种特别的情况,我们需要一些在二叉树基础上的改变。需要在二叉树的基础上加一些限制来合理的改变二叉树结构,让原本可能只形成单只的二叉树得到相对于的处理,使其变换原本的形态,但不改变二叉树的基本限制。使其具有更加方便与搜索等一系列操作的结构。来实现二叉树这种数据结构的更加完美,更能符合各种情况。
这样的话就需要 AVLTree和RBTree来帮助实现。

2、什么是AVL树

由于二叉搜索树在面对一些数据时,会退化并且还会降低搜索效率。因此,俄罗斯的两个数学家G.M.Adelson-Velskii和E.M.Landis 在1962年发明了一种能够解决问题的方法。
当像二叉搜索树中插入节点之后,如果能够保证每个结点左右子树高度绝对值差不超过1(需要进行旋转调整,当超过1的时候),即可降低树的高度,从而减少平均搜索长度,提高搜索效率。
总结来说,AVL树具有以下的特点
1、每一个节点的左右子树都是AVL树
2、左右子树的 高度差/平衡因子 的绝对值不能超过1

此后都会说成平衡因子—右子树高度减去左子树的高度
在这里插入图片描述
这样即使是多么样子的数据,都会成指数倍的减少,大大的减少了搜索的时间,提升了效率。

3、如何设计AVL树

3、1、AVL树节点的定义

由于AVL树的特点,简单二叉树节点的定义反而是不能满足我们AVL树的要求。
由于平衡因子的出现,我们必须找到一个合适的方法来存储平衡因子并且帮助我们能够判断每一个节点的位置是否平衡。
所以,这样的节点设计,我觉得是非常好的。

template<class T>
struct AVLTreeNode
 {
 	AVLTreeNode(const T& data)
     : _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
      ,_data(data), _bf(0)
    {}
 	AVLTreeNode<T>* _pLeft;   // 该节点的左孩子
	AVLTreeNode<T>* _pRight;  // 该节点的右孩子
	AVLTreeNode<T>* _pParent; // 该节点的双亲
	T _data;
    int _bf; 
};

3、2、AVL树的插入

二叉搜索树的插入很简单,AVL树的插入有难但是也有简单的。
插入规则首先按照二叉搜索树那样,把节点插入。然后呢,在考虑节点的平衡因子改变的办法。
比如说如果有一个平衡因子是0的话,无论插入左边还是右边都不会超过AVL树的定义。所以不需要再作别的操作,只需要向上更新节点的平衡因子就行。
但是如果一个节点的平衡因子不是0,而是1或者-1,这两种情况下,只有在一边插入让平衡因子重新回到0才不会有旋转的影响,但是如果平衡因子变成-2或者是2的话,就要进行旋转调整。然后再进行平衡因子的调整。
平衡因子改变的情况:
A)插入父亲节点的左边,父亲节点平衡因子–
B)插入父亲节点右边,父亲节点的平衡因子++

平衡因子改变造成的操作
1、如果父亲节点平衡因子 == 0,那就是说明父亲坐在节点的子树已经到平衡,不需要再向上更新,可以直接结束。
2、如果父亲节点的平衡因子 == -1/1,此时父亲节点高度改变,需要向上更新。
3、如果父亲平衡因子==-2/2,那么意味着父亲所在子树已经不平衡,需要旋转处理

旋转处理是一个很重要的环节,在下面会单独介绍。

//更新平衡因子
while (parent)
		{
			if (cur == parent->_left)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}

			if (parent->_bf == 0) // 1 -1 -> 0
			{
				// 更新结束
				break;
			}
			else if(parent->_bf == 1 || parent->_bf == -1)  // 0 -> 1 -1
			{
				// 继续往上更新
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2) // 1 -1 -> 2 -2
			{
				// 当前子树出问题了,需要旋转平衡一下
				if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == 1)
				{
					RotateL(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}
				
				break;
			}
			else
			{
				// 理论而言不可能出现这个情况
				assert(false);
			}
		}

3、3、平衡因子那些事

关于插入后平衡因子变成-1/1的那些节点不需要太多的关注或者换句话说应该是只需要向上更新到当时的父亲节点的平衡因子为0就能够停止==(如果过程中遇不见平衡因子为-2/2的情况下的话)。==
如果不幸遇到平衡因子是-2/2的情况的话。

3、3、1、平衡因子-2/2下的简单情况

如果说一个平衡因子是-2/2,那么必不可少的是需要旋转,可是旋转过程也是有简单过程的也有复杂过程的。
对于一个左左或者是右右的情况是简单的
什么是左左,右右
简单来说,请看图配合理解。
首先是第一种左左
在这里插入图片描述
其次是右右
在这里插入图片描述
当然这是抽象图,画一个大概,但是其实换一句话说,这也只能通过抽象图来画,因为当h的变化,越来越大的h会有上千上万种可能。
无疑例外需要旋转,对于第一个来说,需要根据RotateR一下,参数是图中的parent节点。
在RotateR之前是先从插入的cur节点开始一步一步向上更新。然后再更新的过程中找到了一个cur的parent节点的平衡因子已经达到了-2/2

void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if(subLR)
			subLR->_parent = parent;

		subL->_right = parent;

		Node* ppNode = parent->_parent;
		parent->_parent = subL;

		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}

			subL->_parent = ppNode;
		}

		parent->_bf = subL->_bf = 0;
	}

其中更新_bf实在所有移动之后进行的。能够蒋将最后一步的_bf程序化进行,那就是说明这最后一步对于所有要左左的情况下旋转都能适应。在这里插入图片描述
那么关于右右的情况的话,也就是和左左相似。在这里插入图片描述

void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		subR->_left = parent;
		Node* ppNode = parent->_parent;

		parent->_parent = subR;

		if (parent == _root)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppNode->_right == parent)
			{
				ppNode->_right = subR;
			}
			else
			{
				ppNode->_left = subR;
			}
			subR->_parent = ppNode;
		}

		parent->_bf = subR->_bf = 0;
	}

3、3、2、平衡因子-2/2下的复杂情况

这种复杂的情况就是和两个简单的相反的情况,就例如说右左,左右这种情况。
为什么这种情况会和之前不一样呢?
就比如说是右左的情况来看一看究竟。
在这里插入图片描述
这就是右左情况之下的单纯的只是使用RotateL和RotateR。这样是会是作茧自缚,到最后还是不能成功解决问题,反而还是回到了最初的状态。
所以此时就需要在特殊情况下的特殊判断的特殊处理!
先将cur节点执行一次RotateR调整一下
在这里插入图片描述
就像这样,只有这样子之后才能到RotateL情况,以parent为参数传入。完成右左的特殊情况。左右的话和右左相差不大,能够根据右左来写出左右的情况。

void RotateRL(Node* parent)
	{
		RotateR(parent->_right);
		RotateL(parent);
	}

4、总结

根据上面的判断,能够得出所有平衡因子不同情况下,如何变为正常的,符合AVL树的定义的操作。
值得注意的是需要旋转,只有在旋转的特殊情况之下,我们才能够解决简单的搜索二叉树的缺点,虽然旋转很难,但是通过旋转的操作,我们能够大大提升对于任何数据的搜索的性能,提高效率。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

tkinter拖入txt文本并显示

tkinter拖入txt文本并显示 效果代码 效果 代码 import tkinter as tk from tkinter import scrolledtext from tkinterdnd2 import DND_FILES, TkinterDnDdef drop(event):file_path event.data.strip({})if file_path.endswith(.txt):with open(file_path, r, encodingutf-8…

K8s 的最后一片拼图:dbPaaS

K8s 的发展使得私有云跟公共云之间的技术差不断的缩小&#xff0c;不管是在私有云还是公共云&#xff0c;大家今天都在基于 K8s 去开发 PaaS 系统。而 K8s 作为构建 PaaS 的基础&#xff0c;其全景图里还缺最后一块“拼图”——dbPaaS。作为一个云数据库行业干了十几年的资深从…

urfread刷算法|构建一棵树

大意 示例标签串&#xff1a; 处理结果&#xff1a; 题目1 根据标签串创建树 需求 需求&#xff1a;给出一个字符串&#xff0c;将这个字符串转换为一棵树。 字符串可以在代码里见到&#xff0c;是以#开头&#xff0c;按照\分割的字符串。 你需要将这个字符串&#xff0…

【鸿蒙学习笔记】@Prop装饰器:父子单向同步

官方文档&#xff1a;Prop装饰器&#xff1a;父子单向同步 [Q&A] Prop装饰器作用 Prop装饰的变量可以和父组件建立单向的同步关系。Prop装饰的变量是可变的&#xff0c;但是变化不会同步回其父组件。 [Q&A] Prop装饰器特点 &#xff11;・Prop装饰器不能在Entry装饰的…

Android Studio上传新项目到Gitee

一、在Gitee上创建仓库 首先需要再Gitee上创建仓库 1、在Gitee中新建仓库 2、输入仓库信息 3、生成仓库地址 创建成功会生成一个仓库地址&#xff0c;格式如下&#xff1a; https://gitee.com/test/compose_mvi_demo.git二、Android Studio 上传项目到Gitee 1、在Android …

CXL-GPU: 全球首款实现百ns以内的低延迟CXL解决方案

数据中心在追求更高性能和更低总拥有成本&#xff08;TCO&#xff09;的过程中面临三大主要内存挑战。首先&#xff0c;当前服务器内存层次结构存在局限性。直接连接的DRAM与固态硬盘&#xff08;SSD&#xff09;存储之间存在三个数量级的延迟差异。当处理器直接连接的内存容量…

Hive测试

1、数据仓库的体系结构包含四个层次&#xff0c;分别是&#xff1a; 数据源 数据存储和管理 数据服务 数据应用 2、Hive提供了类似关系数据库SQL的查询语言&#xff1a; HiveQL 3、Hive某种程度上可以看作 用户编程接口&#xff0c;本身不存储和处理数据&#xff0c;存储数据依…

CesiumJS【Basic】- #057 绘制纹理填充多边形(Primitive方式)

文章目录 绘制纹理填充多边形(Primitive方式)1 目标2 代码2.1 main.ts绘制纹理填充多边形(Primitive方式) 1 目标 使用Primitive方式绘制绘制纹理填充多边形 2 代码 2.1 main.ts import * as Cesium from &

CDC模型

引言 聚类是一种强大的机器学习方法&#xff0c;用于根据特征空间中元素的接近程度发现相似的模式。它广泛用于计算机科学、生物科学、地球科学和经济学。尽管已经开发了最先进的基于分区和基于连接的聚类方法&#xff0c;但数据中的弱连接性和异构密度阻碍了其有效性。在这项…

职业性格测试,企业HR招聘测评最常用人才测评

关于求职测评&#xff0c;招聘中用到的人才测评&#xff0c;你们对这个话题又知道多少呢&#xff1f;为什么我会以90后为分界线&#xff0c;首先90后正是接触计算机最早的一代&#xff0c;因为小编是90后&#xff0c;更了解这个年龄段都在做什么&#xff0c;可以说90后见证了互…

【echarts】拖拽滑块dataZoom-slider自定义样式,简单适配移动端

电脑端 移动端 代码片段 dataZoom: [{type: inside,start: 0,end: 100},{type: slider,backgroundColor: #F2F5F9,fillerColor: #BFCCE3,height: 13, // 设置slider的高度为15start: 0,end: 100,right: 60,left: 60,bottom: 15,handleIcon:path://M30.9,53.2C16.8,53.2,5.3,41.…

第一周题目总结

1.车尔尼有一个数组 nums &#xff0c;它只包含 正 整数&#xff0c;所有正整数的数位长度都 相同 。 两个整数的 数位不同 指的是两个整数 相同 位置上不同数字的数目。 请车尔尼返回 nums 中 所有 整数对里&#xff0c;数位不同之和。 示例 1&#xff1a; 输入&#xff1a…

Android Studio环境搭建(4.03)和报错解决记录

1.本地SDK包导入 安装好IDE以及下好SDK包后&#xff0c;先不要管IDE的引导配置&#xff0c;直接新建一个新工程&#xff0c;进到开发界面。 SDK路径配置&#xff1a;File---->>Other Settings---->>Default Project Structure 拷贝你SDK解压的路径来这&#xff0c;…

自动化任务工具 -- zTasker v1.94 绿色版

软件简介 zTasker 是一款功能强大的自动化任务管理软件&#xff0c;以其简洁易用、一键式操作而著称。软件体积小巧&#xff0c;启动迅速&#xff0c;提供了超过100种任务类型和30多种定时/条件执行方法&#xff0c;能够满足用户在自动化方面的多样化需求。 zTasker 支持定时任…

数据结构 - C/C++ - 树

公开视频 -> 链接点击跳转公开课程博客首页 -> 链接点击跳转博客主页 目录 树的概念 结构特性 树的样式 树的存储 树的遍历 节点增删 二叉搜索树 平衡二叉树 树的概念 二叉树是树形结构&#xff0c;是一种非线性结构。 非线性结构&#xff1a;在二叉树中&#x…

分享一款可编辑本地电脑文件的在线编辑器

背景 之前见过在线版的VSCode&#xff0c;被惊讶到了。网页上竟然可以编辑电脑本地的文件&#xff0c;打破了网页无法编辑本地电脑文件的限制。一直好奇怎么做的。抽空研究了一下&#xff0c;然后发现其实也不难。 分析 先给大家介绍一下这款在线编辑器的效果。 左侧栏为文件…

森马基于MaxCompute+Hologres+DataWorks构建数据中台

讲师&#xff1a;晋银龙 浙江森马数仓高级经理 本次案例主要分享森马集团面对多年自建的多套数仓产品体系&#xff0c;通过阿里云MaxComputeHologresDataWorks统一数仓平台&#xff0c;保障数据生产稳定性与数据质量&#xff0c;减少ETL链路及计算时间&#xff0c;每年数仓整体…

平衡二叉查找树和多路查找树

平衡二叉查找树 普通平衡二叉查找树 平衡二叉树定义是按照有序排列成树状&#xff0c;左子树数据大于右子树&#xff0c;任意节点的左右子树高度不能大于1 优点&#xff1a;可以保证绝对的平衡 缺点&#xff1a;当进行删除节点和新增节点&#xff0c;树进行自平衡的时候&…

计算机网络网络层复习题2

一. 单选题&#xff08;共22题&#xff0c;100分&#xff09; 1. (单选题)如果 IPv4 数据报太大&#xff0c;会在传输中被分片&#xff0c;对分片后的数据报进行重组的是&#xff08; &#xff09;。 A. 中间路由器B. 核心路由器C. 下一跳路由器D. 目的主机 我的答案: D:目的…

RocketMQ源码学习笔记:Producer启动流程

这是本人学习的总结&#xff0c;主要学习资料如下 马士兵教育rocketMq官方文档 目录 1、Overview1.1、创建MQClientInstance1.1.1、检查1.1.1、MQClientInstance的ID 1.2、MQClientInstance.start() 1、Overview 这是发送信息的代码样例&#xff0c; DefaultMQProducer produ…
最新文章