【C++ AVL树】

文章目录

    • AVL树
      • AVL树的概念
      • AVL树节点的定义
      • AVL树的插入
      • AVL树的旋转
        • 右单旋
        • 左单旋
        • 左右双旋
        • 右左双旋
      • 代码实现
    • 总结

AVL树

AVL树的概念

二叉搜索树在顺序有序或接近有序的情况下,而插入搜索树将退化为单叉树,此时查找的时间复杂度为O(n),效率低下。
两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新节点后,保证每个节点的左右子树高度差的绝对值不超过1,即可降低树的高度,减少平均搜索长度。因此,AVL树也被叫做高度平衡二叉搜索树,插入,查找,删除在平均和最坏情况下的时间复杂度都是O( l o g 2 n log_2 n log2n)。

AVL树节点的定义

	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; //balance factor 平衡因子
		AVLTreeNode(const pair<K, V>& kv)
			:_left(nullptr)
			,_right(nullptr)
			,_parent(nullptr)
			,_kv(kv)
			,_bf(0)
		{}
	};

注意:实现AVL树平衡因子不是必须的,只不过有了平衡因子帮助我们更便捷地控制整棵树。

AVL树的插入

  1. 根据二叉搜索树的规则插入新节点
bool Insert(const pair<K, V> & kv)
		{
			root为空,特殊处理
			if (_root == nullptr)
			{
				_root = new Node(kv);
				return true;
			}
			Node* curr = _root;
			Node* parent = nullptr;
			while (curr)
			{
				if (curr->_kv.first < kv.first)
				{
					parent = curr;
					curr = curr->_right;
				}
				else if (curr->_kv.first > kv.first)
				{
					parent = curr;
					curr = curr->_left;
				}
				else
				{
					return false;
				}
			}
			将新节点和其父亲节点链接起来
			Node* newnode = new Node(kv);
			if (parent->_kv.first < kv.first)
				parent->_right = newnode;
			else
				parent->_left = newnode;

			newnode->_parent = parent;
			
			......
		}
  1. 不断向上更新平衡因子
    1. 当前平衡因子为0,说明插入之前平衡因子为1 / -1,插入之后不改变树的高度,不会影响其他祖先节点,此时更新结束。
    2. 当前平衡因子为1 / -1,说明插入之前平衡因子为0,插入之后当前节点地高度发生变化,会影响其他祖先节点,但是不违反规则,需要向上对祖先节点进行更新,直至当前节点为root。
    3. 当前平衡因子为 2 / -2,此时当前节点所在地子树违反了平衡规则,需要进行处理–>旋转。
while (parent)
{
		if (parent->_left == newnode)
		{
			parent->_bf--;
		}
		else
		{
			parent->_bf++;
		}
		if (parent->_bf == 0)
			break;
		else if (parent->_bf == -1 || parent->_bf == 1)
		{
			newnode = parent;
			parent = parent->_parent;
		}
		else if (parent->_bf == -2 || parent->_bf == 2)
		{
			旋转处理
		}
		else
		{
			assert(false);
		}
}

AVL树的旋转

右单旋

在这里插入图片描述

void RotatoR(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;
		subL->_parent = nullptr;
	}
	else
	{
		if (ppnode->_left == parent)
			ppnode->_left = subL;
		else
			ppnode->_right = subL;
		subL->_parent = ppnode;
	}
	parent->_bf = 0;
	subL->_bf = 0;
}
左单旋

在这里插入图片描述

void RotatoL(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;
		subR->_parent = nullptr;
	}
	else
	{
		if (ppnode->_left == parent)
			ppnode->_left = subR;
		else
			ppnode->_right = subR;
		subR->_parent = ppnode;
	}
	parent->_bf = 0;
	subR->_bf = 0;
}
左右双旋

在这里插入图片描述
旋转之前,45的平衡因子可能是-1/0/1,旋转完成之后,根据情况对其他节点的平衡因子进行调整

void RotatoLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	int bf = subLR->_bf;
	RotatoL(subL);
	RotatoR(parent);
	subLR->_bf = 0;
	if (bf == 0)
	{
		subL->_bf = 0;
		parent->_bf = 0;
	}
	else if (bf == 1)
	{
		subL->_bf = -1;
		parent->_bf = 0;
	}
	else if (bf == -1)
	{
		subL->_bf = 0;
		parent->_bf = 1;
	}
}
右左双旋

在这里插入图片描述

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

	RotatoR(subR);
	RotatoL(parent);
	if (bf == 0)
	{
		subR->_bf = 0;
		parent->_bf = 0;
	}
	else if (bf == 1)
	{
		subR->_bf = 0;
		parent->_bf = -1;
	}
	else if (bf == -1)
	{
		subR->_bf = 1;
		parent->_bf = 0;
	}
}

代码实现

namespace xxx
{
	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; //balance factor 平衡因子
		AVLTreeNode(const pair<K, V>& kv)
			:_left(nullptr)
			,_right(nullptr)
			,_parent(nullptr)
			,_kv(kv)
			,_bf(0)
		{}
	};
	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* curr = _root;
			Node* parent = nullptr;
			while (curr)
			{
				if (curr->_kv.first < kv.first)
				{
					parent = curr;
					curr = curr->_right;
				}
				else if (curr->_kv.first > kv.first)
				{
					parent = curr;
					curr = curr->_left;
				}
				else
				{
					return false;
				}
			}
			Node* newnode = new Node(kv);
			if (parent->_kv.first < kv.first)
				parent->_right = newnode;
			else
				parent->_left = newnode;
			newnode->_parent = parent;
			while (parent)
			{
				if (parent->_left == newnode)
				{
					parent->_bf--;
				}
				else
				{
					parent->_bf++;
				}
				if (parent->_bf == 0)
					break;
				else if (parent->_bf == -1 || parent->_bf == 1)
				{
					newnode = parent;
					parent = parent->_parent;
				}
				else if (parent->_bf == -2 || parent->_bf == 2)
				{
					if (parent->_bf == 2 && newnode->_bf == 1)
					{
						RotatoL(parent);
					}
					else if (parent->_bf == -2 && newnode->_bf == -1)
					{
						RotatoR(parent);
					}
					else if (parent->_bf == -2 && newnode->_bf == 1)
					{
						RotatoLR(parent);
					}
					else if (parent->_bf == 2 && newnode->_bf == -1)
					{
						RotatoRL(parent);
					}
					else
					{
						assert(false);
					}
					break;
				}
				else
				{
					assert(false);
				}
			}
			return true;
		}
		void RotatoL(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;
				subR->_parent = nullptr;
			}
			else
			{
				if (ppnode->_left == parent)
					ppnode->_left = subR;
				else
					ppnode->_right = subR;
				subR->_parent = ppnode;
			}
			parent->_bf = 0;
			subR->_bf = 0;
		}
		void RotatoR(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;
				subL->_parent = nullptr;
			}
			else
			{
				if (ppnode->_left == parent)
					ppnode->_left = subL;
				else
					ppnode->_right = subL;
				subL->_parent = ppnode;
			}
			parent->_bf = 0;
			subL->_bf = 0;
		}
		void RotatoLR(Node* parent)
		{
			Node* subL = parent->_left;
			Node* subLR = subL->_right;
			int bf = subLR->_bf;
			RotatoL(subL);
			RotatoR(parent);
			subLR->_bf = 0;
			if (bf == 0)
			{
				subL->_bf = 0;
				parent->_bf = 0;
			}
			else if (bf == 1)
			{
				subL->_bf = -1;
				parent->_bf = 0;
			}
			else if (bf == -1)
			{
				subL->_bf = 0;
				parent->_bf = 1;
			}
		}
		void RotatoRL(Node* parent)
		{
			Node* subR = parent->_right;
			Node* subRL = subR->_left;
			int bf = subRL->_bf;
			subRL->_bf = 0;

			RotatoR(subR);
			RotatoL(parent);
			if (bf == 0)
			{
				subR->_bf = 0;
				parent->_bf = 0;
			}
			else if (bf == 1)
			{
				subR->_bf = 0;
				parent->_bf = -1;
			}
			else if (bf == -1)
			{
				subR->_bf = 1;
				parent->_bf = 0;
			}
		}
		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}
		bool IsAVLTree()
		{
			return _IsAVLTree(_root);
		}
	private:
		bool _IsAVLTree(Node* root)
		{
			if (root == nullptr)
				return true;
			int leftH = Height(root->_left);
			int rightH = Height(root->_right);
			return abs(leftH - rightH) <= 1
				&& _IsAVLTree(root->_left)
				&& _IsAVLTree(root->_right);
		}
		int Height(Node* node)
		{
			if (node == nullptr)
				return 0;
			int leftH = Height(node->_left);
			int rightH = Height(node->_right);
			return 1 + max(leftH, rightH);
		}
		void _InOrder(Node* root)
		{
			if (root == nullptr)
				return;
			_InOrder(root->_left);
			cout << root->_kv.second << ' ';
			_InOrder(root->_right);
		}
		Node* _root = nullptr;
	};
}

总结

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即 l o g 2 ( N ) log_2 (N) log2(N)。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

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

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

相关文章

Java通过jedis连接redis一些常用方法

小伙伴们好&#xff0c;欢迎关注&#xff0c;一起学习&#xff0c;无线进步 以下内容为学习redis过程中的一些笔记 文章目录 Jedis常用API判断keyStringListSetHashZset事务 Jedis 使用 Java 来操作 Redis&#xff0c;知其然并知其所以然 什么是Jedis 是 Redis 官方推荐的 jav…

#WEB前端(DIV、SPAN)

1.实验&#xff1a;DIV、SPAN 2.IDE&#xff1a;VSCODE 3.记录&#xff1a; 类? 4.代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdev…

【java、微服务、spring】SpringCloud

服务拆分 1. 不同微服务&#xff0c;不要重复开发相同业务 2&#xff0e;微服务数据独立&#xff0c;不要访问其它微服务的数据库 3&#xff0e;微服务可以将自己的业务暴露为接口&#xff0c;供其它微服务调用 远程调用 提供者与消费者 服务提供者&#xff1a;一次业务中…

扼杀网络中的环路:STP、RSTP、MSTP

目录 前言&#xff1a; 一、STP&#xff08;Spanning Tree Protocol&#xff09; 1.1 STP功能 1.2 STP应用 二、RSTP&#xff08;Rapid Spanning Tree Protocol&#xff09; 2.1 RSTP功能 2.2 RSTP应用 三、MSTP&#xff08;Multiple Spanning Tree Protocol&#xff0…

ScanDomainEuorg:批量查询 eu.org 域名注册情况(附带源码)

引言 eu.org 很长时间都没有审批了&#xff0c;但是我觉得只是时间长短问题&#xff0c;早晚会再次审批的。 既然如此&#xff0c;大可以未雨绸缪一般&#xff0c;趁着大家对其“失望”的时间段&#xff0c;看看有哪些好看的前缀没有被注册。 原理 灵感来源于 域名 .eu.org…

Java 网络面试题解析

1. Http 协议的状态码有哪些&#xff1f;含义是什么&#xff1f;【重点】 200&#xff1a;OK&#xff0c;客户端请求成功。 301&#xff1a;Moved Permanently&#xff08;永久移除&#xff09;&#xff0c;请求的URL已移走。Response中应该包含一个Location URL&#xff0c;…

Thinkphp框架漏洞--->5.0.23 RCE

1.Thinkphp ThinkPHP是一个免费开源的&#xff0c;快速、简单的面向对象的轻量级PHP开发框架&#xff0c;是为了敏捷WEB应用开发和简化 企业应用开发而诞生的。 2.漏洞原理及成因 该漏洞出现的原因在于 ThinkPHP5框架底层对控制器名过滤不严 &#xff0c;从而让攻击者可以通过…

QoS简单配置案例

1、两边两个方向做相同的配置&#xff1a;入口复杂流分类用mqc方式配置&#xff0c;ds内设备入口配简单流分类。 2、两边两个方法做拥塞管理配置&#xff0c;拥塞管理配置思路&#xff1a; 拥塞管理的两种配置方法&#xff08;全部用一种也可以&#xff0c;这里学习就用了两种…

Vue3 条件渲染 v-if

v-if 指令&#xff1a;用于控制元素的显示或隐藏。 执行条件&#xff1a;当条件为 false 时&#xff0c;会将元素从 DOM 中删除。 应用场景&#xff1a;适用于显示隐藏切换频率较低的场景。 语法格式&#xff1a; <div v-if"数据">内容</div> 基础用…

解决 MySQL 未运行但锁文件存在的问题

查看mysql状态时&#xff0c;显示错误信息"ERROR! MySQL is not running, but lock file (/var/lock/subsys/mysql) exists"。 解决步骤 1、检查 MySQL 进程是否正在运行 在继续之前&#xff0c;我们首先需要确定 MySQL 进程是否正在运行。我们可以使用以下命令检查…

离线数仓(四)【数仓数据同步策略】

前言 今天来把数仓数据同步解决掉&#xff0c;前面我们已经把日志数据到 Kafka 的通道打通了。 1、实时数仓数据同步 关于实时数仓&#xff0c;我们的 Flink 直接去 Kafka 读取即可&#xff0c;我们在学习 Flink 的时候也知道 Flink 提供了 Kafka Source&#xff0c;所以这里不…

前端学习第二天-html提升

达标要求 了解列表的分类 熟练掌握列表的用法 熟练掌握表格的结构构成 合并单元格 表单的组成 熟练掌握表单控件分类的使用 1.列表 1.1 无序列表 <ul>&#xff1a;定义无序列表&#xff0c;并且只能包含<li>子元素。 <li>&#xff1a;定义列表项&a…

【kubernetes VPA】记录一次安装 VPA 相关组件的报错解决过程

文章目录 1. 问题描述2. 问题原因3. 解决办法4. 参考链接 1. 问题描述 在执行 ./hack/vpa-up.sh脚本命令时&#xff0c;提示有报错。名为vpa-admission-controller的容器状态一直停留在ContainerCreating&#xff0c;从该Pod详细描述中得知&#xff0c;volume "tls-certs…

【自动驾驶技术系列丛书学习】1.《自动驾驶技术概论》学习笔记

《自动驾驶技术概论》学习笔记 致谢&#xff1a;作者&#xff1a;王建、徐国艳、陈竞凯、冯宗宝 本书主要介绍汽车构造和无人驾驶汽车的基本概念&#xff0c;从基础开始&#xff0c;由浅入深地了解无人驾驶的历史由来、国内外自动驾驶产业现状及技术发展、自动驾驶汽车的技术架…

2025张宇考研数学,百度网盘视频课+36讲PDF讲义+真题

张宇老师的课属于幽默生动&#xff0c;会让一个文科生爱上数学&#xff0c;但是有的同学不知道在哪看&#xff0c;可以看一下&#xff1a;2025张宇考研数学全程网盘 docs.qq.com/doc/DTmtOa0Fzc0V3WElI 可以粘贴在浏览器 张宇30讲作为一本基础讲义&#xff1a;和教材…

【第二十五课】动态规划:完全背包问题(acwing-3 / 公式推导 / 思路理解 / 优化 / c++代码)

目录 思路 朴素代码 优化 公式推导上 二维代码 一维代码 公式理解上 在开始看完全背包问题之前&#xff0c;可能需要先了解01背包及其解决办法。 指路&#x1f447; 【第二十五课】动态规划&#xff1a;01背包问题(acwing-2 / 思路 / 含一维数组优化 / c代码) 思路 …

代码随想录算法刷题训练营day30:LeetCode(332)重新安排行程、LeetCode(51)n-皇后、LeetCode(37)解数独

代码随想录算法刷题训练营day30&#xff1a;LeetCode(332)重新安排行程、LeetCode(51)n-皇后、LeetCode(37)解数独 LeetCode(332)重新安排行程 题目 代码 //第二次刷题---在刷--高难度---注意超时---该代码照着代码随想录卡哥编写的代码写的&#xff0c;题目难度过大&#…

【随记】分享第1期(2024.03.02)

记录这段时间&#xff0c;看到的有趣/有用/值得分享的东西 灵感来源&#xff1a;分类&#xff1a;周刊 - 阮一峰的网络日志 (ruanyifeng.com) 文章目录 大佬博客实用工具文章文摘 大佬博客 云风的 BLOG (codingnow.com) 美团技术团队 (meituan.com) 计算机科学 – 刘未鹏 | Mi…

19.2 DeepMetricFi:基于深度度量学习改进Wi-Fi指纹定位

P. Chen and S. Zhang, "DeepMetricFi: Improving Wi-Fi Fingerprinting Localization by Deep Metric Learning," in IEEE Internet of Things Journal, vol. 11, no. 4, pp. 6961-6971, 15 Feb.15, 2024, doi: 10.1109/JIOT.2023.3315289. 摘要 Wi-Fi RSSI指纹定位…

批次大小对ES写入性能影响初探

问题背景 ES使用bulk写入时每批次的大小对性能有什么影响&#xff1f;设置每批次多大为好&#xff1f; 一般来说&#xff0c;在Elasticsearch中&#xff0c;使用bulk API进行批量写入时&#xff0c;每批次的大小对性能有着显著的影响。具体来说&#xff0c;当批量请求的大小增…
最新文章