AVL树介绍

一、介绍

高度平衡的搜索二叉树,保证每个节点的左右子树高度差不超过1,降低搜索树的高度以提高搜索效率。

通过平衡因子和旋转来保证左右子树高度差不超过1

二、插入节点

1、插入规则

(1)搜按索树规则插入节点

(2)更新父节点平衡因子

插入左边就 --,插入右边就 ++

(3)保证父节点平衡因子不超过1

平衡因子是0:表示插入节点之后以父节点为子树的树高度没变,所以插入合法直接跳出。

平衡因子是正负1:以父节点为子树的树高度增大或者减小了1,虽然当前的父节点依旧合法,但是由于高度变了还要向上更新平衡因子。

平衡因子是正负2:要旋转使以父节点为子树的树平衡因子合法。合法后直接挑出。

2、旋转逻辑实现

(1)左高右低 右单旋

旋转前:

旋转后:

大逻辑:把 subLR 给 parent,把 parent 给 subL

右单旋代码实现逻辑和注意点:

a、标记三个节点:subL,subLR,ppNode(保存 parent 的父亲,为了最后更新 subL 的父节点)

b、先 subLR 作 parent 的左子树,注意更新subLR 父节点时 subLR == nullptr 情况

c、再 parent 作 subL 的右子树,注意更新 parent 的父节点

d、利用 ppNode 更新subL 的位置和父节点,注意一开始父节点是根的情况

e、更新 subL 和 parent 的平衡因子为0

右单旋代码:

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

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

	subL->_right = parent;
	parent->_parent = subL;

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

(2)右高左低 左单旋

旋转前:

旋转后:

分析过程和右单旋类似,代码如下:

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

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

	subR->_left = parent;
	parent->_parent = subR;

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

(3)左高右高 左右双旋

旋转图:

左右双旋的本质就是把 subLR 的左子树给 subL,右子树给 parent

通过本质就可以知道三种不同情况的平衡因子:

              插入前 subLR 平衡因子             插入后平衡因子
subLR subLparent 
-1(新节点插在 subLR 的左子树)001
1(新节点插在 subLR 的右子树)0-10
0(新节点就是 subLR)000

左右双旋代码:

void RotateLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	int bf = subLR->_bf;

	RotateL(subL);
	RotateR(parent);

	if (bf == -1)
	{
		subL->_bf = 0;
		parent->_bf = 1;
		subLR->_bf = 0;
	}
	else if (bf == 1)
	{
		subL->_bf = -1;
		parent->_bf = 0;
		subLR->_bf = 0;
	}
	else if (bf == 0)
	{
		subL->_bf = 0;
		parent->_bf = 0;
		subLR->_bf = 0;
	}
	else
		assert(false);
}

(4)右高左高 右左双旋

旋转图:

右左双旋的本质就是把 subRL 的左子树给 parent,右子树给 subR

通过本质就可以知道三种不同情况的平衡因子:

              插入前 subRL 平衡因子             插入后平衡因子
subRL subRparent 
-1(新节点插在 subRL 的左子树)010
1(新节点插在 subRL 的右子树)00-1
0(新节点就是 subRL)000

右左双旋代码:

void RotateRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	int bf = subRL->_bf;

	RotateR(subR);
	RotateL(parent);

	if (bf == 1)
	{
		subR->_bf = 0;
		parent->_bf = -1;
		subRL->_bf = 0;
	}
	else if (bf == -1)
	{
		subR->_bf = 1;
		parent->_bf = 0;
		subRL->_bf = 0;
	}
	else if (bf == 0)
	{
		subR->_bf = 0;
		parent->_bf = 0;
		subRL->_bf = 0;
	}
	else
		assert(false);
}

3、插入代码展示

bool insert(const pair<K, V> kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		return true;
	}

	Node* cur = _root;
	Node* parent = cur;
	// 1.像搜索二叉树一样插入节点cur
	while (cur)
	{
		if (cur->_kv.first > kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_kv.first < 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;

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

		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)
				RotateLR(parent);
			// 右高左高右左双旋
			else
				RotateRL(parent); 
			break;
		}
		else
			assert(false);
	}
	return true;
}

三、验证AVL树

检查每个节点的左右子树高度差是否不超过1和平衡因子是否是左右子树的高度差

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

	int leftHeight = _Height(root->_left);
	int rightHeight = _Height(root->_right);
	// 不平衡
	if (abs(leftHeight - rightHeight) >= 2)
	{
		cout << root->_kv.first << endl;
		return false;
	}

	// 顺便检查一下平衡因子是否正确
	if (rightHeight - leftHeight != root->_bf)
	{
		cout << root->_kv.first << endl;
		return false;
	}

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

四、删除节点

这里只了解删除逻辑,具体如何旋转不考虑。

删除规则

按搜索树删除

删除并更新平衡因子

删除左边就 ++,删除右边就 --

保证父节点平衡因子不超过1

平衡因子是0:表示删除结点之前是正负1,删除后高度改变,所以要向上继续更新。

平衡因子是正负1:表示删除结点之前是0,删除后高度不变,所以跳出。

平衡因子是正负2:要旋转使以父节点为子树的树平衡因子合法。合法后直接跳出。

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

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

相关文章

win11 sourcetree安装问题

win11 sourcetree安装出现msys-2.0.dll 问题&#xff0c;需要从win10的以下路径复制出 msys-2.0.dll来加入到win11中 C:\Users\kz121468\AppData\Local\Atlassian\SourceTree\git_local\usr\bin\ 复制到 win11的 C:\Users\kz121468\AppData\Local\Atlassian\SourceTree\git_lo…

Contrastive Imitation Learning

机器人模仿学习中对比解码的一致性采样 摘要 本文中&#xff0c;我们在机器人应用的对比模仿学习中&#xff0c;利用一致性采样来挖掘演示质量中的样本间关系。通过在排序后的演示对比解码过程中&#xff0c;引入相邻样本间的一致性机制&#xff0c;我们旨在改进用于机器人学习…

Spring Web MVC基础第一篇

目录 1.什么是Spring Web MVC&#xff1f; 2.创建Spring Web MVC项目 3.注解使用 3.1RequestMapping&#xff08;路由映射&#xff09; 3.2一般参数传递 3.3RequestParam&#xff08;参数重命名&#xff09; 3.4RequestBody&#xff08;传递JSON数据&#xff09; 3.5Pa…

DeepSeek的使用技巧介绍

DeepSeek是一款由杭州深度求索人工智能技术有限公司开发的AI工具&#xff0c;结合了自然语言处理和深度学习技术&#xff0c;能够完成多种任务&#xff0c;如知识问答、数据分析、文案创作、代码开发等。以下将从使用技巧、核心功能及注意事项等方面详细介绍DeepSeek的使用方法…

创新创业计划书|建筑垃圾资源化回收

目录 第1部分 公司概况........................................................................ 1 第2部分 产品/服务...................................................................... 3 第3部分 研究与开发.................................................…

为AI聊天工具添加一个知识系统 之80 详细设计之21 符号逻辑 之1

本文要点 要点 前面我们讨论了本项目中的正则表达式。现在我们将前面讨论的正则表达式视为狭义的符号文本及其符号规则rule&#xff08;认识的原则--认识上认识对象的约束&#xff09;&#xff0c;进而在更广泛的视角下将其视为符号逻辑及其符号原则principle&#xff08;知识…

Spring Boot 热部署实现指南

在开发 Spring Bot 项目时&#xff0c;热部署功能能够显著提升开发效率&#xff0c;让开发者无需频繁重启服务器就能看到代码修改后的效果。下面为大家详细介绍一种实现 Spring Boot 热部署的方法&#xff0c;同时也欢迎大家补充其他实现形式。 步骤一、开启 IDEA 自动编译功能…

ARM嵌入式学习--第十一天(中断处理 , ADC)

--中断的概念 中断是指计算机运行过程中&#xff0c;出现某些意外情况需主机干预时&#xff0c;机器能自动停止正在运行的程序并转入处理新情况的程序&#xff0c;处理完毕后又返回被暂停的程序继续运行 --CPU处理事情的方式 -轮询方式 不断查询是否有事情需要处理&#xff0c…

ARM嵌入式学习--第十天(UART)

--UART介绍 UART(Universal Asynchonous Receiver and Transmitter)通用异步接收器&#xff0c;是一种通用串行数据总线&#xff0c;用于异步通信。该总线双向通信&#xff0c;可以实现全双工传输和接收。在嵌入式设计中&#xff0c;UART用来与PC进行通信&#xff0c;包括与监控…

socket实现HTTP请求,参考HttpURLConnection源码解析

背景 有台服务器&#xff0c;网卡绑定有2个ip地址&#xff0c;分别为&#xff1a; A&#xff1a;192.168.111.201 B&#xff1a;192.168.111.202 在这台服务器请求目标地址 C&#xff1a;192.168.111.203 时必须使用B作为源地址才能访问目标地址C&#xff0c;在这台服务器默认…

漏洞扫描工具之xray

下载地址&#xff1a;https://github.com/chaitin/xray/releases 1.9.11 使用文档&#xff1a;https://docs.xray.cool/tools/xray/Scanning 与burpsuite联动&#xff1a; https://xz.aliyun.com/news/7563 参考&#xff1a;https://blog.csdn.net/lza20001103/article/details…

正月初三特殊的一天

在我们河南豫东地区&#xff0c;初三这一天一般情况下可以在家休息&#xff0c;不需要串门走亲戚&#xff0c;给亲戚的长辈或比自己辈份长的拜年。 特殊的正月初三 还有两种情况&#xff0c;正月初三这一天必须去走亲戚。一种是有去世的亲戚没有过三周年&#xff0c;正月初三这…

强化学习笔记——4策略迭代、值迭代、TD算法

基于策略迭代的贝尔曼方程和基于值迭代的贝尔曼方程&#xff0c;关系还是不太理解 首先梳理一下&#xff1a; 通过贝尔曼方程将强化学习转化为值迭代和策略迭代两种问题 求解上述两种贝尔曼方程有三种方法&#xff1a;DP&#xff08;有模型&#xff09;&#xff0c;MC&#xff…

HTTP协议和静态web服务器

一、HTTP协议 1 HTTP协议的定义 网络协议 网络协议是指计算机通信网络中两台计算机之间进行通信所必须共同遵守的规定或规则。HTTP协议 HTTP协议(超文本传输协议)是一种网络通信协议,它允许将超文本标记语言(HTML)文档从Web服务器传送到客户端的浏览器。默认端口:80HTTPS协…

智能汽车网络安全威胁报告

近年来随着智能汽车技术的快速发展&#xff0c;针对智能汽车的攻击也逐渐从传统的针对单一车辆控制器的攻击转变为针对整车智能化服务的攻击&#xff0c;包括但不限于对远程控制应用程序的操控、云服务的渗透、智能座舱系统的破解以及对第三方应用和智能服务的攻击。随着WP.29 …

在虚拟机里运行frida-server以实现对虚拟机目标软件的监测和修改参数(一)(android Google Api 35高版本版)

frida-server下载路径 我这里选择较高版本的frida-server-16.6.6-android-x86_64 以root身份启动adb 或 直接在android studio中打开 adb root 如果使用android studio打开的话&#xff0c;最好选择google api的虚拟机&#xff0c;默认以root模式开启 跳转到下载的frida-se…

Node.js——body-parser、防盗链、路由模块化、express-generator应用生成器

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

【2024年华为OD机试】(C卷,200分)- 启动多任务排序 (JavaScriptJava PythonC/C++)

一、问题描述 题目解析 本题是一个典型的拓扑排序问题。拓扑排序用于解决有向无环图(DAG)中的节点排序问题,使得对于图中的每一条有向边 (u, v),u 在排序中总是位于 v 的前面。在本题中,任务之间的依赖关系可以看作是有向图中的边,而任务的执行顺序就是拓扑排序的结果。…

【NLP251】NLP RNN 系列网络

NLP251 系列主要记录从NLP基础网络结构到知识图谱的学习 &#xff11;.原理及网络结构 &#xff11;.&#xff11;&#xff32;&#xff2e;&#xff2e; 在Yoshua Bengio论文中( http://proceedings.mlr.press/v28/pascanu13.pdf )证明了梯度求导的一部分环节是一个指数模型…

Privacy Eraser,电脑隐私的终极清除者

Privacy Eraser 是一款专为保护用户隐私而设计的全能型软件&#xff0c;它不仅能够深度清理计算机中的各类隐私数据&#xff0c;还提供了多种系统优化工具&#xff0c;帮助用户提升设备的整体性能。通过这款软件&#xff0c;用户可以轻松清除浏览器历史记录、缓存文件、Cookie、…