hello树先生——AVL树

news2024/11/15 21:55:16

AVL树

  • 一.什么是AVL树
  • 二.AVL树的结构
    • 1.AVL树的节点结构
    • 2.插入函数
    • 3.旋转调整
  • 三.平衡测试

一.什么是AVL树

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
AVL树的性质:

  • 它的左右子树都是AVL树
  • 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
  • 如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 O ( l o g 2 n ) O(log_2 n) O(log2n)
    在这里插入图片描述

二.AVL树的结构

1.AVL树的节点结构

template<class K, class V>
struct AVLTreeNode
{
	AVLTreeNode* _left;
	AVLTreeNode* _right;
	AVLTreeNode* _parent;
	pair<K, V> _kv;
	int _bf;

	AVLTreeNode(const pair<K,V>& kv)
		:_left(nullptr),
		_right(nullptr),
		_parent(nullptr),
		_kv(kv),
		_bf(0)
	{}
};

这里的数据存储使用pair类型的数据对,将key索引与对应数据value存储,增加了_parent保存父亲节点,增加了_bf平衡因子来衡量左右子树是否平衡。

2.插入函数

bool insert(const pair<K, V>& kv)

这里插入方式与搜索二叉树一致,只是在后面增加了平衡调整的步骤

//如果根为空
		if (_root == nullptr)
		{
			_root = new node(kv);
			return true;
		}

		//找到目标值
		node* parent = nullptr;
		node* cur = _root;
		while (cur)
		{
			//key < cur
			if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				//查重
				return false;
			}
		}

		//插入目标值
		cur = new node(kv);

		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

调节平衡因子

while(parent)
{
//向上更新
}

如果插在左子树,则平衡因子–,否则++


			if (parent->_left == cur)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}

判断平衡因子是否正常倘若>=2则需要旋转

			if (parent->_bf == 0)
			{
				break;
			}
			else if(parent->_bf == -1 || parent->_bf == 1)
			{
				//向上调整
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == -2 || parent->_bf == 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
				{
					rotatelr(parent);
				}
				break;
			}
			else
			{
				//大问题
				assert(false);
			}

3.旋转调整

AVL树的旋转分为四种,左单旋,右单旋,左右双旋和右左双旋。

1.右单旋

	void rotater(node* parent)

在这里插入图片描述
当插入new节点时,60节点会左右失衡,我们称60为parent,30为subl,则平衡因子会分别变为-2,-1,成为左左高模型,此时使用右单旋。
由于搜索树特性:30<b<60<c,所以可以将右方降下去,从而达到平衡。

  • 传入旋转点parent

  • 记录关键节点,防止旋转后丢失
    在这里插入图片描述

		node* subl = parent->_left;
		node* sublr = subl->_right;
  • 将parent的左指向sublr,若sublr为空节点,则不链接父亲节点否则链接
  • 将subl的右指向parent,同时链接父亲节点
		parent->_left = sublr;
		if (sublr)
			sublr->_parent = parent;

		subl->_right = parent;
		parent->_parent = subl;
		//保存父节点
		node* ppnode = parent->_parent;
		subl->_parent = ppnode;
  • 考虑parent是否为根,是否链接parent的父亲节点
		//考虑parent是否为根
		if (ppnode == nullptr)
		{
			_root = subl;
			subl->_parent = nullptr;
		}
		else
		{
			subl->_parent = ppnode;
			if (ppnode->_left == parent)
			{
				ppnode->_left = subl;
			}
			else
			{
				ppnode->_right = subl;
			}
		}
  • 修改平衡因子
parent->_bf = subl->_bf = 0;

2.左单旋
大体思路与右单旋相同
在这里插入图片描述

	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;
		subr->_parent = ppnode;

		//考虑parent是否为根
		if (ppnode == nullptr)
		{
			_root = subr;
			subr->_parent = nullptr;
		}
		else
		{
			subr->_parent = ppnode;
			if (ppnode->_left == parent)
			{
				ppnode->_left = subr;
			}
			else
			{
				ppnode->_right = subr;
			}
		}
		//调节平衡因子
		parent->_bf = subr->_bf = 0;

	}

3.左右双旋

void rotatelr(node* parent)

通常双旋有三种情况,在b里插入新节点,在c里插入新节点,或者b,c都为空
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

记录sul的平衡因子,方便判断是哪种情况

		node* subl = parent->_left;
		node* sublr = subl->_right;
		int bf = subl->_bf;

		rotatel(subl);
		rotater(parent);
		if (bf == -1)
		{
			subl->_bf = 0;
			parent = 1;
		}
		else if (bf == 1)
		{
			subl->_bf = -1;
			parent->_bf = 0;
		}
		else if (bf == 0)
		{
			subl->_bf = 0;
			parent->_bf = 0;
		}
		sublr->_bf = 0;

每一种情况平衡因子不同,所以需要画图分别讨论
** 4.右左双旋**

void rotaterl(node* parent)
	{
		node* subr = parent->_right;
		node* subrl = subr->_left;
		int bf = subr->_bf;

		rotater(subr);
		rotater(parent);

		

		if (bf == -1)
		{
			subr->_bf = 1;
			parent = 0;
		}
		else if (bf == 1)
		{
			subr->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == 0)
		{
			subr->_bf = 0;
			parent->_bf = 0;
		}
		subrl->_bf = 0;

	}

三.平衡测试

判断是否平衡

    bool _IsBalance(node* root)
	{
		if (root == nullptr)
		{
			return true;
		}

		int leftheight = _height(root->_left);
		int rightheight = _height(root->_right);

		if (abs(leftheight - rightheight) >= 2)
		{
			return false;
		}

		//判断节点平衡因子是否正确

		if (root->_bf != (rightheight - leftheight))
		{
			return false;
		}

		return _IsBalance(root->_left) && _IsBalance(root->_right);
	}

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

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

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

相关文章

【计组 | Cache原理】讲透Cache的所有概念与题型方法

Cache 写在前面&#xff1a;高速缓存Cache一直408中的重点以及绝对的难点&#xff0c;前几天我在复习计组第三章的知识&#xff0c;Cache这一节把我困住了&#xff0c;我发现很多概念我都不记得了&#xff0c;一些综合性强的计算题根本无从下手&#xff0c;我深知Cache对于每个…

1分钟把高质量AI知识库站点嵌入小程序

许多企业都有把 AI 知识库装进小程序、网站、企业微信、钉钉等的需求&#xff0c;让用户能够在小程序上访问到高品质的内容。奈何有太多限制&#xff0c;往往会遇到IP地址不被信任或技术对接接口配置等困难。HelpLook能帮你节省这些繁琐的程序&#xff0c;0代码快速将AI知识库站…

工程师们都爱看的Docker容器技术,一看就会!保姆级教程(上)

文章目录 Docker简介Docker在企业中的应用场景Docker与虚拟化的对比Docker的优势 部署Docker部署DockerDocker的基本操作Docker镜像管理容器的常用操作 Docker镜像构建Docker镜像结构镜像运行的基本原理镜像获得方式镜像构建Docker镜像构建企业实例 镜像优化方案镜像优化策略镜…

一款免费强大的快速启动工具,快速打开程序,软件,网站,工具等

Lucy是一款由个人开发者针对个人需求开发的快速启动工具&#xff0c;其最大的特点在于简洁和快速。它允许用户通过简单的拖拽操作将文件、文件夹、网址等添加到启动列表中&#xff0c;实现快速访问常用程序和文件的目的。Lucy不依赖于网络连接&#xff0c;避免了隐私泄露的风险…

Xcode插件开发

Xcode插件开发 文章目录 Xcode插件开发一、插件开发流程创建插件Extension文件介绍文件说明 二、插件使用安装说明 一、插件开发流程 创建插件的过程并不复杂&#xff0c;只是官方教程&#xff0c;过于简单&#xff0c;所以这里补充下创建细节 创建插件 环境&#xff1a;Xco…

公安智慧大楼信息化整体建设设计方案

1. 项目背景与需求分析 《公安智慧大楼信息化整体建设设计方案》针对一个用地面积和建筑面积均具规模的建设项目&#xff0c;提出了信息化建设的全方位设计方案&#xff0c;以满足现代公安业务的需求。 2. 信息化设计理念 方案强调了信息化设计的顶层设计方法论&#xff0c;…

【Qt】窗口概述

Qt 窗口概述 Qt窗口是由QMianWindow类来实现的。 QMainWindow 是⼀个为⽤⼾提供主窗⼝程序的类&#xff0c;继承⾃ QWidget 类&#xff0c;并且提供了⼀个预定义的布局。QMainWindow 包含 ⼀个菜单栏&#xff08;menu bar&#xff09;、多个⼯具栏(tool bars)、多个浮动窗⼝&a…

基于Swagger自动生成离线API文档(Word、Markdown文档)

在做项目时通常需要给客户提供离线Word的API文档归档&#xff0c;不要跟客户说有Swagger在线API文档&#xff0c;客户不会用也不会去看。只要你有Swagger&#xff0c;TableGo就能自动生成一份漂亮的Word离线API文档给客户&#xff0c;大大提高了写文档的效率&#xff0c;客户看…

【0-1背包】3180. 执行操作可获得的最大总奖励 I

给你一个整数数组 rewardValues&#xff0c;长度为 n&#xff0c;代表奖励的值。 最初&#xff0c;你的总奖励 x 为 0&#xff0c;所有下标都是 未标记 的。你可以执行以下操作 任意次 &#xff1a; 从区间 [0, n - 1] 中选择一个 未标记 的下标 i。 如果 rewardValues[i] 大…

openwrt结合智能家居(相关搜索:路由器)

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

如何在华为平板上开发一个web网页

首先进入华为应用市场下载Qpython 下载完成后打开,在主页里面进入pip,然后下载flask 下载完成后创建一个Python文件,输入 然后运行,进入了Qpython终端后输入pyth,然后就可以开始开发web网页了。 请各位老手们多多指点

10:USB模块布局

1.先处理差分线 先开上方飞线&#xff1a; 发现有个晶体&#xff1a; ①晶体有随身电容&#xff0c;随身电阻 ②晶体布局思路&#xff1a; –电容和电阻放在ic和晶体之间 –整个晶体的部分靠近IC – 信号走向&#xff1a;IC—>电阻—>电容(左右两边摆)—>晶体 开右…

基于Pytorch框架的深度学习HRnet网络人像语义分割系统源码

第一步&#xff1a;准备数据 头发分割数据&#xff0c;总共有5711张图片&#xff0c;里面的像素值为0和1&#xff0c;所以看起来全部是黑的&#xff0c;不影响使用 第二步&#xff1a;搭建模型 计算机视觉领域有很多任务是位置敏感的&#xff0c;比如目标检测、语义分割、实例…

做影像组学+深度学习技术研究如何发表高分论文,案例解析

论文简介 标题&#xff1a;Longitudinal MRI-based fusion novel model predicts pathological complete response in breast cancer treated with neoadjuvant chemotherapy: a multicenter, retrospective study&#xff08;纵向MRI结合新模型预测新辅助化疗乳腺癌的病理完全…

[C++进阶]map和set的相关题目

1. 下面关于map和set说法错误的是&#xff08;&#xff09; A.map中存储的是键值对&#xff0c;set中只储存了key B.map和set查询的时间复杂度都是O(log_2N) C.map和set都重载了[]运算符 D.map和set底层都是使用红黑树实现的 解析: A&#xff1a;正确&#xff0c;map和set的概念…

python学习之路 - PySpark快速入门

目录 一、PySpark实战1、前言介绍2、基础准备a、pySpark库的安装b、构建pySpark执行环境入口对象c、pySpark编程模型 3、数据输入a、python数据容器转RDD对象b、读取文件内容转RDD对象 4、数据计算a、map算子b、flatMap算子c、reduceByKey算子d、综合案例e、filter算子f、disti…

数据可视化库(Matplotlib)

目录 常规绘图方法 细节设置 子图和标注 风格设置 常用图表绘制 盒图 直方图和散点图 3D图 布局设置 常规绘图方法 首先导入工具包&#xff0c;一般用plt来当作Matplotlib的别名&#xff1a; import matplotlib.pyplot as plt %matplotlib inline 指定魔法指令之后…

刚刚放出GPT-5上线时间,转身就一个限制,OpenAI你真行!

大家好&#xff0c;我是AI肝铁侠。 在6月23日&#xff0c;OpenAI 首席技术官米拉穆拉蒂 (Mira Murati) 表示&#xff0c;GPT-5 是 OpenAI 的下一代人工智能产品&#xff0c;预计将在 2025 年底或 2026 年初&#xff0c;实现博士级别的智能。 说实话OpenAI又把GPT5计划发布时间…

Leetcode面试经典150题-45.跳跃游戏II

解法都在代码里&#xff0c;不懂就留言或者私信&#xff0c;这个题绝对比动态规划的解法强 class Solution {/**本题我们先不用动态规划了&#xff0c;因为从任何一个位置都可能跳到最后一个位置&#xff0c;用动态规划的成本太高了本题的解题思路&#xff1a;看看某个步数内最…

Vue项目安装依赖(npm install)报错的解决

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…