C++手撕AVL树

文章目录

    • AVL树
      • 概念
    • 节点
    • 插入
      • 右单旋
      • 左右双旋
    • 验证AVL树
    • AVL树的性能

AVL树

之前我们讲了二叉搜索树的相关内容,但是也了解到二叉搜索树有其自身的缺陷,就是当插入的元素有序或者接近有序,退化成单支树的时候,他的时间复杂度就会退化成O(N),因此我们需要对他进行优化,有两种,一种是AVL树,也就是平衡二叉树,另一种是红黑树,这里我们先介绍AVL树

概念

在发现这个问题之后,两个俄罗斯数学家发明了解决这个问题的一种办法

当向二叉搜索树中插入节点之后,检查每个节点的左右子树高度只差绝对值不超过一,否则需要进行调整

这样就能降低树的高度,从而减少平均的搜索长度

一棵AVL树是空树或者有如下性质

  • 左右子树都是AVL树
  • 左右子树高度差(平衡因子)的绝对值不超过1

例如

image.png

节点

template<class K, class V>
struct AVLTreeNode {
	AVLTreeNode<K, V>* _left; // 左子树
	AVLTreeNode<K, V>* _right; // 右子树
	AVLTreeNode<K, V>* _parent; // 父节点

	pair<K, V> _kv; // 键值对

	int _bf; // 平衡因子

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

插入

AVL树与二叉搜索树的区别就是引入了平衡因子,而在插入的时候就需要考虑到

但是插入时就要考虑到破坏平衡因子的条件,我们就需要进行调整,也就是说分两步,按照二叉搜索树的方式插入节点,再对树进行调整

我们用右子树高度减去左子树高度作为该树的平衡因子,因此当左子树插入一个新节点高度加一时,平衡因子减一即可,右子树插入一个新节点高度加一时,平衡因子加一即可

当平衡因子更新之后,就有可能出现不平衡的状态了

  1. 更新后为0,说明更新之前为正负1,插入后调整为0,此时不需要沿父节点向上更新平衡因子
  2. 更新后为正负1,说明更新前为0,插入后调整为正负1,此时需要沿父节点向上更新平衡因子
  3. 更新之后为正负2,此时不平衡,需要进行旋转处理

此时我们就需要对旋转进行分情况讨论了,有四大类情况

右单旋

原本的状态如下

image.png

插入一个新节点

image.png

右单旋

image.png

具体操作是将30的右子树给60的左子树,然后让30做60的父节点,之后更新平衡因子,这里有几点需要考虑,30的右子树有可能不存在,但是不影响结果

60如果是根节点,旋转完成之后需要转换根节点给30,如果是子树,则需要判断他是他父节点的左子树还是右子树,需要更新

左单旋和右单旋情况类似,这里不过多展开

左右双旋

当新插入的节点在较高左子树的右侧时,旋转一次就无法解决问题了,此时就需要先左旋,再右选,如图

image.png

插入节点

image.png

先以30为中心左单旋

image.png

再以90为中序右单旋

image.png

旋转之后再更新平衡因子即可

同理的,如果是右子树较高的左子树插入,则需要先右旋再左旋

template<class K, class V>
class AVLTree {
	typedef AVLTreeNode<K, V> Node;
public:
	bool Insert(const pair<K, V>& kv) {
		if (_root == nullptr) { // 空树直接插入
			_root = new Node(kv);
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;

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

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

		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) {
					RotateL(parent);
				} else if (parent->_bf == -2 && cur->_bf == -1) {
					RotateR(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);
			}
		}

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

			parent->_right = subRL;
			subR->_left = parent;

			Node* parentParent = parent->_parent;

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

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

				subR->_parent = parentParent;
			}

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

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

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

			Node* parentParent = parent->_parent;

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

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

				subL->_parent = parentParent;
			}

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

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

			RotateR(parent->_right);
			RotateL(parent);

			if (bf == 0) {
				// subRL自己就是新增
				parent->_bf = subR->_bf = subRL->_bf = 0;
			} else if (bf == -1) {
				// subRL的左子树新增
				parent->_bf = 0;
				subRL->_bf = 0;
				subR->_bf = 1;
			} else if (bf == 1) {
				// subRL的右子树新增
				parent->_bf = -1;
				subRL->_bf = 0;
				subR->_bf = 0;
			} else {
				assert(false);
			}
		}
		return true;
	}
private:
	Node* _root = nullptr;
};

验证AVL树

要验证AVL树有两步

  1. 中序遍历得到有序序列
  2. 验证每个节点的平衡因子的绝对值不大于1,如果没有平衡因子则计算高度差

AVL树的性能

AVL树的查询性能在 log ⁡ 2 N \log_2N log2N,但是如果要更改结构时性能就比较擦汗了,尤其是当旋转次数比较多时,更差的是在删除时,有可能每次都要旋转,因此需要一种查询高效切有序而且数据个数不怎么改变的时候,可以考虑AVL树,不断插入和删除则不合适

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

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

相关文章

AIGC: 4 IT从业者如何构建自己的AI知识体系

图片是我使用dall.e模型生成的图片&#xff0c; 提示词&#xff1a; 程序员系统学习OpenAI开发者平台系统学习。 我按照SCQA模型&#xff0c;来开始今天的内容。 S 场景 今天是2024年3月23日&#xff0c;我在深圳&#xff0c;从事IT行业&#xff0c;每个人从事的行业各不相…

redis启动后无法被外部主机连接

目录 一、场景二、连接异常三、排查四、原因五、解决 一、场景 1、CentOS安装redis后&#xff0c;外部主机无法连接到redis 二、连接异常 1、RedisDesktopManager无法连接 2、使用telnet命令测试6379端口是否能正常访问 三、排查 1、redis服务是否启动 四、原因 从以下信息…

前端学习之JavaScript基础语法三种引入方式、三种输出方式、输入框、确认框、循环加强、arguments

目录 三种引入方式 三种输出方式 运行结果 变量 确认框、输入框 运行结果 循环加强 arguments 三种引入方式 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Document</title><!-- 三…

MySQL索引优化二

分页查询优化 很多时候我们的业务系统实现分页功能可能会用如下sql实现 select * from employees limit 10000,10;表示从表employees中取出从10001行开始的10条记录.看似只查询了10条记录,实际这条sql是先读取10010条记录,然后抛弃前10000条记录,然后读到后面10条想要的数据,…

蓝鹏智能测量仪应用于这些方面!助力发展新质生产力!

新质生产力是未来几年着重发展的方向&#xff0c;关于如何实现产业化升级&#xff0c;各厂家会在自身的基础上进行产业化调整升级&#xff0c;利用新工具、新手段&#xff0c;大幅缩短研发设计周期&#xff0c;从而让产品迭代速度不断加快&#xff1b;提升产品品质&#xff0c;…

防静电检测设备如何完善PCBA车间的防静电管控?

在PCBA&#xff08;Printed Circuit Board Assembly&#xff09;车间中&#xff0c;静电是一个极其重要的问题&#xff0c;因为静电可能对电子元器件和PCB板造成损坏&#xff0c;进而影响整个生产流程和产品质量。为了有效防止静电问题&#xff0c;企业通常会引入防静电检测设备…

UE5学习日记——蓝图节点前缀关键字整理

一、起因 节点如海&#xff0c;中英文翻译的时候还是有差别的&#xff0c;比如&#xff1a; 同一个中文&#xff0c;可能在英文里完全不同&#xff0c;连出现位置可能都不一样 附加 Attach Actor To Component&#xff08;将Actor附加到组件&#xff09;Append Array&#xf…

CTF题型 nodejs(1) 命令执行绕过典型例题

CTF题型 nodejs(1) 命令执行绕过 文章目录 CTF题型 nodejs(1) 命令执行绕过一.nodejs中的命令执行二.nodejs中的命令绕过1.编码绕过2.拼接绕过3.模板字符串4.Obejct.keys5.反射6.过滤中括号的情况典型例题1.[GFCTF 2021]ez_calc2.[西湖论剑 2022]Node Magical Login 一.nodejs中…

SpringBoot可以同时处理多少请求

SpringBoot默认的内嵌容器是Tomcat&#xff0c;即看Tomcat可以处理多少请求 默认配置 server:tomcat:threads:min-spare: 10 # 最小工作线程数max: 200 # 最大线程数max-connections: 8192 # 接受和处理的最大连接数&#xff0c;超过8192的请求就会被放入到等待队列中ac…

【原创教程】关于东方马达的控制方法(上)

1 实现的功能 能够精准定位,快速移动到指定位置 2 硬件配置 东方马达组件一套包含:AZD-CD驱动器,AZM66MC马达电机。 如下图所示: 2.1 东方马达I/O端子分配 2.2 电路图 2.3 硬件接线

代码随想录算法训练营第二十一天|530. 二叉搜索树的最小绝对差

530. 二叉搜索树的最小绝对差 已解答 简单 相关标签 相关企业 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&#xff0c;其数值等于两值之差的绝对值。 示例 1&#xff1a; 输入&#xff1a;root [4,2,6,1,3] 输出…

rfc793-page36

rfc793原文 If the connection is in any non-synchronized state (LISTEN,SYN-SENT, SYN-RECEIVED), and the incoming segment acknowledgessomething not yet sent (the segment carries an unacceptable ACK), orif an incoming segment has a security level or compart…

Redis数据类型bitMap以及解决的相关实际需求

在Redis数据库中&#xff0c;Bitmap&#xff08;位图&#xff09;是一种特殊的数据结构&#xff0c;它不是一个独立的数据类型&#xff0c;而是基于String类型实现的。Bitmap主要用于存储大量二进制位&#xff08;0或1&#xff09;的数据&#xff0c;这些位可以代表不同的状态或…

CMU-TARE 探索算法官方社区问答汇总

参考引用 TARE机器人自主导航系统社区-CSDN社区云TARE平台资源链接汇总CMU团队开源算法点云地面分割 terrainAnalysis 代码解析Local Planner 代码详解以及如何适用于现实移动机器人论文翻译&#xff1a;Autonomous Exploration Development Environment and the Planning Algo…

3.学习前后端关联

目录 1.接口类型 2.错误状态码 3.如何定义路由 4.那如何要求前端传入一个JSON数据呢&#xff1f; 4.解决前后端口不同源,跨域问题 1.使用CrossOrigin 2.直接复制代码使用 5.用户登录校验 1.接口类型 POST(新增数据)、PUT(更新更改数据)、GET(查询)、DELET(删除数据) …

day05 设计计算机硬件

嵌入式学习-04_嵌入式技术之从零搭建计算机 1 添加立即数 现有系统的数据RAM存储方式(操作码+操作数)。 地址指令opcode(操作码)addr(操作数)新代码 /数据000ld_a0b000010b1000b0000100000000100001add0b000100b1010b0001000000000101010sub0b000110b1100b000110000000…

搭建PHP本地开发环境:看这一篇就够了

什么是PHP本地开发环境 PHP本地开发环境是指在个人计算机上模拟的服务器环境&#xff0c;这使得开发者能够在没有网络连接的情况下也能开发、测试和调试PHP应用程序。就像在你的电脑里装个小“服务器”&#xff0c;即使没网也能搞定PHP程序的开发和修修补补。这就是PHP本地开发…

【微服务】接口幂等性常用解决方案

一、前言 在微服务开发中&#xff0c;接口幂等性问题是一个常见却容易被忽视的问题&#xff0c;同时对于微服务架构设计来讲&#xff0c;好的幂等性设计方案可以让程序更好的应对一些高并发场景下的数据一致性问题。 二、幂等性介绍 2.1 什么是幂等性 通常我们说的幂等性&…

自定义类型

在之前的博客中我们讲到了C语言有三种自定义类型&#xff1a;结构体&#xff08;结构&#xff09;、枚举和联合&#xff0c;在这篇博客中我们将更加深入地探讨这三种自定义类型。 结构体 1.结构体的声明 struct tag {int a;char ch;int arr[3];double d;float f; }t1,t2;如上…

2022 年甘肃省职业院校技能大赛 高职组 网络系统管理竞赛 网络构建模块试题

2022 年甘肃省职业院校技能大赛 高职组网络系统管理竞赛 网络构建模块试题 目 录 考试说明… 3 任务描述… 3 任务清单… 3 &#xff08;一&#xff09;基础配置… 3 &#xff08;二&#xff09;有线网络配置… 4 &#xff08;三&#xff09;无线网络配置… 6 &#xff08;四&a…