AVL树浅谈

前言

大家好,我是jiantaoyab,本篇文章给大家介绍AVL树。

基本概念

AVL树(Adelson-Velsky和Landis树)是一种自平衡的二叉搜索树,得名于其发明者G. M. Adelson-Velsky和E. M. Landis。在AVL树中,任何节点的两个子树的高度最大差别为1,因此它也被称为高度平衡树。

AVL树特点

  1. 二叉搜索树性质:AVL树本质上是一棵二叉搜索树,即每个节点的左子树中的所有节点的值都小于该节点的值,而右子树中的所有节点的值都大于该节点的值。
  2. 平衡条件:AVL树的每个节点的左子树和右子树的高度差之绝对值不超过1。这是AVL树与其他二叉搜索树的主要区别,保证了树的平衡性,从而优化了查找、插入和删除操作的性能。
  3. 平衡因子:每个节点都有一个平衡因子,定义为该节点的右子树高度减去左子树高度。在AVL树中,平衡因子的取值只能是-1、0或1。

模拟实现

AVL树节点定义

template<class K,class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V> *_left;
	AVLTreeNode<K, V> *_right;
	AVLTreeNode<K, V> *_parent;
	pair<K,V> _kv;  //pair是将2个数据组合成一组数据

	int _bf; //balance factor

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

};

插入操作

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;
			}

		
		//控制平衡
		//1、更新平衡因子 
		//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;
			}

			//旋转处理 
			//1:树平衡了
			//2:树的整体高度降了1
			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 if (parent->_bf == 2 && cur->_bf == -1){
					RotateRL(parent);
				}

				else{
					assert(false);
				}
				//旋转完之后不影响上面的,可以直接退出
				break;	

			}

			//插入之前,平衡因子有问题
			else{
				assert(false);
			}

		}

		return true;
}

旋转操作

右单旋

image-20240506110209062

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

		parent->_left = subLR;
		subL->_right = parent;

		//更新父节点
		if (subLR)
			subLR->_parent = parent;
		Node* parentParent = parent->_parent;
		parent->_parent = subL;

		//如果原来是根
		if (parent == _root){
			_root = subL;
			_root->_parent = nullptr;
		}
		//如果是别人的子树
		else{
			if (parentParent->_left == parent)
				parentParent->_left = subL;
			else
				parentParent->_right = subL;

			subL->_parent = parentParent;
		}

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

左单旋

void RotateL(Node* parent)
	{

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

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

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

		//原来是根
		if (_root == parent){
			_root = subR;
			subR->_parent = nullptr;
		}
		//原来是别人的子树
		else{
			if (parentParent->_left == parent)
				parentParent->_left = subR;
			else
				parentParent->_right = subR;
			subR->_parent = parentParent;
		}

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

左右双旋

image-20240506123437496

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

			RotateL(parent->_left);
			RotateR(parent);
			if (bf == -1)
			{
				parent->_bf = 1;
				subL->_bf = 0;
			}

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

右左双旋

void RotateRL(Node *parent)	
	{
		//   30 (parent)
		//  /   \
		//  a   90(subR)
		//      /   \     
		//    60(subRL)   d
		//   b  c
	
		Node *subR = parent->_right;
		Node *subRL = subR->_left;

		int bf = subRL->_bf;

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

		//这里看图 变化后的图
		if (bf == 1){
			parent->_bf = -1;
			subR->_bf = 0;
			subRL->_bf = 0;
		}

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

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

	}

判断是不是AVL树

int Height(Node *root)
	{
		if (root == NULL)
			return 0;

		int leftHeight = Height(root->_left);
		int rightHeight = Height(root->_right);
		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

bool IsBalance()
	{
		return _IsBalance(_root);
	}

	//1.检查是不是搜索二叉树
	//2.检查每一课子树是不是AVL树
	bool _IsBalance(Node *root)
	{
		if (root == NULL)
			return true;

		//当前树检查
		int leftHeight = Height(root->_left);
		int rightHeight = Height(root->_right);

		//平衡因子出问题
		if (rightHeight - leftHeight != root->_bf){
			cout << root->_kv.first << "NOW::" << root->_bf << endl;
			cout << root->_kv.first << "CORRECT::" << rightHeight - leftHeight << endl;
			return false;
		}

		//左右高度差不能超过2
		return abs(rightHeight - leftHeight) < 2 && _IsBalance(root->_left)
			&& _IsBalance(root->_right);
	}

AVL树性能

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即O(log n)。

但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。

因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

AVL树使用场景

  1. 数据库和文件系统的索引
    • 数据库系统经常需要快速检索、插入和删除记录。AVL树作为索引结构,可以加速这些操作,确保查询效率不会因为数据的不均匀分布而降低。
  2. 内存数据库
    • 对于需要快速响应的内存数据库,如Redis,使用AVL树可以确保数据访问的高效性。
  3. 字典或词汇查找
    • 在自然语言处理或文本编辑器中实现自动补全、拼写检查或同义词查找等功能时,AVL树可以提供快速的词汇查找和插入。
  4. 路由表查找
    • 在计算机网络中,路由器需要根据路由表快速查找最佳路径。AVL树可以确保路由查找的高效性。
  5. 搜索引擎
    • 搜索引擎在处理大量网页索引时需要快速检索和更新索引。AVL树可以帮助优化这些操作。
  6. 缓存系统
    • 在实现缓存替换策略(如LRU,即最近最少使用策略)时,AVL树可以帮助维护一个有序的缓存项列表,从而快速确定哪些项应该被替换。
  7. 事件处理系统
    • 在需要按时间顺序处理事件的系统(如日历应用或任务调度器)中,AVL树可以用于维护一个有序的事件列表。
  8. 科学计算和模拟
    • 在科学计算和模拟中,经常需要快速查找、插入或删除数据点。AVL树可以提供一个高效的数据结构来支持这些操作。
  9. 金融交易系统
    • 实现自动补全、拼写检查或同义词查找等功能时,AVL树可以提供快速的词汇查找和插入。

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

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

相关文章

活动回放 | 如何进行全增量一体的异构数据库实时同步

以 AI领域为代表的新技术不断涌现&#xff0c;新的应用风口也逐渐清晰。为了加紧跟上技术发展的步伐&#xff0c;越来越多的企业开始着手&#xff0c;对仍以传统关系型数据库为主的应用后端进行现代化升级。 这就涉及到如何在不影响并保持现有业务系统正常运转的前提下&#xf…

专注 APT 攻击与防御—基于UDP发现内网存活主机

UDP简介&#xff1a; UDP&#xff08;User Datagram Protocol&#xff09;是一种无连接的协议&#xff0c;在第四层-传输层&#xff0c;处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点&#xff0c;也就是说&#xff0c;当报文发送之后&#xf…

Android 状态栏WiFi图标的显示逻辑

1. 状态栏信号图标 1.1 WIFI信号显示 WIFI信号在状态栏的显示如下图所示 当WiFi状态为关闭时&#xff0c;状态栏不会有任何显示。当WiFi状态打开时&#xff0c;会如上图所示&#xff0c;左侧表示有可用WiFi&#xff0c;右侧表示当前WiFi打开但未连接。 当WiFi状态连接时&#x…

SpringBoot整合rabbitmq使用案例

RocketMQ&#xff08;二十四&#xff09;整合SpringBoot SpringBoot整合rabbitmq使用案例 一 SpringBoot整合RocketMQ实现消息发送和接收消息生产者1&#xff09;添加依赖2&#xff09;配置文件3&#xff09;启动类4&#xff09;测试类 消息消费者1&#xff09;添加依赖2&…

python 中如何匹配字符串

python 中如何匹配字符串&#xff1f; 1. re.match 尝试从字符串的起始位置匹配一个模式&#xff0c;如果不是起始位置匹配成功的话&#xff0c;match()就返回none。 import re line"this hdr-biz 123 model server 456" patternr"123" matchObj re.matc…

iconfont_vue小程序中使用

1.前三步就是简单下载&#xff0c;详细可看这篇 vue管理系统导航中添加新的iconfont的图标-CSDN博客 2.引用有点区别&#xff1a;在App中引用 3.uni-icons写法 <uni-icons custom-prefix"iconfont" type"icon-zhengjian" size"23"></un…

情感视频素材怎么来的?8个视频素材库免费下载安装

在今天这个视觉内容对于连接和影响观众至关重要的时代&#xff0c;选择适合的视频素材变得极为关键。优质的视频素材可以极大提升您的内容质量&#xff0c;无论是在增加社交媒体的吸引力、提升商业广告的效果&#xff0c;还是丰富教育材料的表现力。以下是一些全球顶级的视频素…

基于 Llama-Index、Llama 3 和 Qdrant,构建一个 RAG 问答系统!

构建一个使用Llama-Index、Llama 3和Qdrant的高级重排-RAG系统 尽管大型语言模型&#xff08;LLMs&#xff09;有能力生成有意义且语法正确的文本&#xff0c;但它们面临的一个挑战是幻觉。 在LLMs中&#xff0c;幻觉指的是它们倾向于自信地生成错误答案&#xff0c;制造出看似…

Stateflow基础知识笔记

01--Simulink/Stateflow概述 Stateflow是集成于Simulink中的图形化设计与开发工具&#xff0c;主要 用于针对控制系统中的复杂控制逻辑进行建模与仿真&#xff0c;或者说&#xff0c; Stateflow适用于针对事件响应系统进行建模与仿真。 Stateflow必须与Simulink联合使用&#…

一个年薪30w软件测试员的职业规划,献给还在迷茫中的朋友

先抛出一个观点 &#xff0c; 那些&#xff0c;担心30岁后&#xff0c;35岁后&#xff0c;40岁后&#xff0c;无路可走的&#xff1b;基本属于能力不够、或者思维太局限 。 总之&#xff0c;瞎担心 / 不长进 。 具体&#xff0c;见下面正文 。 曾经&#xff0c;在16年&#xff…

开发环境待

一 web开发环境搭建 1 web开发环境概述 所谓web开发,指的就是从网页中向后端程序发送请求.与后端程序进行交互. 流程图: 1,Web服务器是指驻留于因特网上某种类型计算机的程序. 2, 可以向浏览器等Web客户端提供文档&#xff0c;也可以放置网站文件&#xff0c;让全世界 浏览…

JWK和JWT 学习

JWK和JWT 介绍 JWK (JSON Web Key) 和 JWT (JSON Web Token) 是现代Web应用程序中用于安全通信的两个重要概念。它们都是基于JSON的&#xff0c;并且是OAuth 2.0和OpenID Connect等协议的核心组成部分。 官方文档 JWT官方网站 JWK和JWK Set的RFC文档 JWT的RFC文档 JWK (JS…

16_Scala面向对象编程_函数

文章目录 1.声明Scala函数2.访问伴生对象3.空对象直接用的方法4.构造对象--通过object获取单例对象--直接new--scala独有apply()方式--scala有参构造--scala构造方法两大类使用辅构造如下上述代码主构造为辅助构造方法甚至可以多个多个辅助构造形参内容不能重不使用辅助构造和使…

【ACM出版】第四届控制与智能机器人国际学术会议(ICCIR 2024)

第四届控制与智能机器人国际学术会议&#xff08;ICCIR 2024&#xff09; 2024 4th International Conference on Control and Intelligent Robotics 2024年6月21日-23日 | 中国-广州 官网&#xff1a;www.ic-cir.org EI、Scopus双检索 投稿免费参会、口头汇报及海报展示 四…

ROS仿真小车与SLAM

ROS仿真小车与SLAM ROS中机器小车的仿真实验一、建立模型1.创建功能包导入依赖&#xff1a;创建urdf,launch文件&#xff1a; 2.可视化 二、添加雷达传感器1.编写xacro文件2.集成launch文件3.添加摄像头和雷达传感器my_camera.urdf.xacro文件&#xff1a;my_laser.urdf.xacro文…

easy_signin_ctfshow_2023愚人杯

https://ctf.show/challenges#easy_signin-3967 2023愚人杯信息检索&#xff0c;在请求荷载中发现一个base64 face.pngencode ZmFjZS5wbmc解密结果 flag.pngencode ZmxhZy5wbmc尝试一下 返回内容 Warning: file_get_contents(flag.png): failed to open stream: No such file…

AArch64 内存管理

本文是对arm developer网站《Learn the architecture - AArch64 memory management Guide》的学习笔记&#xff08;Documentation – Arm Developer&#xff09; 一、背景概述 本文介绍了AArch64中的内存转换&#xff0c;这是内存管理的关键&#xff0c;它解释了虚拟地址如何转…

成语:势如破竹、迎刃而解;明以前唯一同时入选文庙、武庙的牛人

千古流芳、身后能够进入文庙或武庙&#xff0c;是古人最高的荣誉&#xff0c;也是读书人和武将终极的追求&#xff0c;所谓的青史留名&#xff0c;享受万代祭祀、千秋敬仰&#xff0c;甚至都可以称之为圣人&#xff0c;但历史上&#xff0c;却有两人文武兼备、同时入选了文庙与…

单调栈-java

本次主要通过数组模拟单调栈来解决问题。 目录 一、单调栈☀ 二、算法思路☀ 1.暴力做法&#x1f319; 2.优化做法&#x1f319; 3.单调递增栈和单调递减栈&#x1f319; 三、代码如下☀ 1.代码如下&#xff08;示例&#xff09;&#xff1a;&#x1f319; 2.读入数据&a…

学习记录:AUTOSAR R20-11的阅读记录(一)【Foundation(FO)】

一、OverView 1、AUTOSAR R20-11文档下载 官网下载&#xff1a;AUTOSAR 打包文档地址&#xff1a;AUTOSAR R20-11 2、文档组说明 AUTOSAR定义了三个文档组&#xff1a;ClassicPlatform(CP)、Adaptive Platform(AP)和Foundation(FO)&#xff0c;基于CP和AP的ECU基于共同标准F…