暴力数据结构——AVL树

news2024/11/24 1:22:46

1.认识AVL树

AVL树最先发明的⾃平衡⼆叉查找树,AVL可以是⼀颗空树,或者具备下列性质的⼆叉搜索树:

它的左右⼦树都是AV树,且左右⼦树的⾼度差的绝对值不超过1

AVL树是⼀颗⾼度平衡搜索⼆叉树, 通过控制⾼度差去控制平衡 

AVL树整体结点数量和分布和完全⼆叉树类似,⾼度可以控制在logN,那么增删查改的效率也可以控制在O(logN),相⽐⼆叉搜索树有了本质的提升 

平衡因子:为了在插入时更加方便可以对每个节点设置一个平衡因子,当然这不是必要的但是可以提升效率,平衡因子这里的概念是:该节点的右子树高度 - 该节点左子树高度

2.AVL树的结构

//节点
template<class K, class V>
struct AVLTreeNode
{
      //需要parent指针,后续更新平衡因⼦可以看到 
      pair<K, V> _kv;
      AVLTreeNode<K, V>* _left;
      AVLTreeNode<K, V>* _right;
      AVLTreeNode<K, V>* _parent;
      int _bf; // balance factor
      AVLTreeNode(const pair<K, V>& kv)
      :_kv(kv)
      , _left(nullptr)
      , _right(nullptr)
      , _parent(nullptr)
      ,_bf(0)
      {}
};

//整棵树
template<class K, class V>
class AVLTree
{
   typedef AVLTreeNode<K, V> Node;
public:
private:
   Node* _root = nullptr;
};

3.AVL树的插入

AVL树插⼊⼀个值的⼤概过程

1. 插⼊⼀个值按⼆叉搜索树规则进⾏插⼊

2. 新增结点以后,只会影响祖先结点的⾼度,也就是可能会影响部分祖先结点的平衡因⼦,所以更新从新增结点->根结点路径上的平衡因⼦,实际中最坏情况下要更新到根,有些情况更新到中间就可以停⽌了,具体情况我们下⾯再详细分析

3. 更新平衡因⼦过程中没有出现问题,则插⼊结束

4. 更新平衡因⼦过程中出现不平衡,对不平衡⼦树旋转,旋转后本质调平衡的同时,本质降低了⼦树的⾼度,不会再影响上⼀层,所以插⼊结束

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;
	}
	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)
		{
			// 不平衡了,旋转处理 
			break;
		}
		else
		{
			assert(false);
		}
	}
	return true;
}

3.1平衡因子更新

• 更新后parent的平衡因⼦等于0,更新中parent的平衡因⼦变化为-1->0或者1->0,说明更新前 parent⼦树⼀边⾼⼀边低,新增的结点插⼊在低的那边,插⼊后parent所在的⼦树⾼度不变,不会影响parent的⽗亲结点的平衡因⼦,更新结束

• 更新后parent的平衡因⼦等于1或-1,更新前更新中parent的平衡因⼦变化为0->1或者0->-1,说明更新前parent⼦树两边⼀样⾼,新增的插⼊结点后,parent所在的⼦树⼀边⾼⼀边低,parent所在的⼦树符合平衡要求,但是⾼度增加了1,会影响parent的⽗亲结点的平衡因⼦,所以要继续向上更新

• 更新后parent的平衡因⼦等于2或-2,更新前更新中parent的平衡因⼦变化为1->2或者-1->-2,说明更新前parent⼦树⼀边⾼⼀边低,新增的插⼊结点在⾼的那边,parent所在的⼦树⾼的那边更⾼了,破坏了平衡,parent所在的⼦树不符合平衡要求,需要旋转处理,旋转的⽬标有两个:1.把 parent⼦树旋转平衡。2.降低parent⼦树的⾼度,恢复到插⼊结点以前的⾼度。所以旋转后也不需要继续往上更新,插⼊结束

3.2右单旋

根节点的平衡因子为-2,即左子树深度过深,需要将左子树旋转,我们将根节点命名为parent,根节点的左节点命名为subL,该左节点的右节点命名为subLR,我们需要进行的操作是首先将subLR接入parent的左节点,然后判断subLR是否为空,不为空则将其父节点指针指向parent,之后就是将subL调整为调整子树的根节点即可,最后更新平衡因子

//右单旋
void RotateR(Node* parent)
{
	//subL代表根节点的左子树,subLR则代表该左子树的右子树
	Node* subL = parent->_left;
	Node* subLR = subL->_right;

	parent->_left = subLR;
	//避免访问空节点
	if (subLR)
	{
		subLR->_parent = parent;
	}
	
	//如果传入的根节点是一个子树的根节点则需要将旋转后的根节点重新接入原来的节点
	Node* pParent = parent->_parent;

	//换根,将原来根节点的左节点换为根节点
	subL->_right = parent;
	parent->_parent = subL;

	if (pParent == nullptr)
	{
		_root = subL;
		//根节点的父亲节点为空
		subL->_parent = nullptr;
	}
	else
	{
		if (pParent->_left == parent)
		{
			pParent->_left = subL;
		}
		else
		{
			pParent->_right = subL;
		}
		//重新接入原来的节点
		subL->_parent = pParent;
	}
	//更新平衡因子,因为只改变了这两个父节点,所以只更新他们即可
	subL->_bf = 0;
	parent->_bf = 0;
}

3.3左单旋

 单旋可以看做是右单旋的对称操作

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

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

	Node* pParent = parent->_parent;

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

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

3.4左右单旋

• 场景1:h>=1时,新增结点插⼊在e⼦树,e⼦树⾼度从h-1并为h并不断更新8->5->10平衡因⼦, 引发旋转,其中8的平衡因⼦为-1,旋转后8和5平衡因⼦为0,10平衡因⼦为1

• 场景2:h>=1时,新增结点插⼊在f⼦树,f⼦树⾼度从h-1变为h并不断更新8->5->10平衡因⼦,引发旋转,其中8的平衡因⼦为1,旋转后8和10平衡因⼦为0,5平衡因⼦为-1

• 场景3:h==0时,a/b/c都是空树,b⾃⼰就是⼀个新增结点,不断更新5->10平衡因⼦,引发旋转,其中8的平衡因⼦为0,旋转后8和10和5平衡因⼦均为0

//左右双旋
void RotateLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;

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

	int bf = subLR->_bf;
	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);
	}
}

3.5右左单旋

• 场景1:h>=1时,新增结点插⼊在e⼦树,e⼦树⾼度从h-1变为h并不断更新12->15->10平衡因⼦,引发旋转,其中12的平衡因⼦为-1,旋转后10和12平衡因⼦为0,15平衡因⼦为1

• 场景2:h>=1时,新增结点插⼊在f⼦树,f⼦树⾼度从h-1变为h并不断更新12->15->10平衡因⼦, 引发旋转,其中12的平衡因⼦为1,旋转后15和12平衡因⼦为0,10平衡因⼦为-1

• 场景3:h==0时,a/b/c都是空树,b⾃⼰就是⼀个新增结点,不断更新15->10平衡因⼦,引发旋转,其中12的平衡因⼦为0,旋转后10和12和15平衡因⼦均为0

//右左双旋
void RotateRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;

	RotateR(subR);
	RotateL(parent);

	int bf = subRL->_bf;

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

4.AVL树的判断

关于判断AVL树就是判断其是否每个节点平衡因子正确,并且保证每个节点的平衡因子的绝对值不能超过2,不然就不是一个AVL树,最后返回结果即可 

//判断是否为AVL树
//计算树的高度
int _Height(Node* root)
{
	if (root == nullptr)
	{
		return 0;
	}
	
	int leftheight = _Height(root->_left);
	int rightheight = _Height(root->_right);

	return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
}

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

	int leftheight = _Heighr(root->_left);
	int rightheight = _Height(root->_right);
	int diff = rightheight - leftheight;

	if (abs(diff) >= 2)
	{
		cout << root->_kv.first << "高度差异常" << endl;
		return false;
	}
	if (diff != root->_bf)
	{
		cout << root->_kv.first << "平衡因子异常" << endl;
	}

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

5.AVL树的查找 

按照二叉搜索树查找即可,时间复杂度为O(logN)

Node* Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_kv.first < key)
		{
			cur = cur->_right;
		}
		else if (cur->_kv.first > key)
		{
			cur = cur->_left;
		}
		else
		{
			return cur;
		}
	}
	return nullptr;
}

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

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

相关文章

路由交换实验指南

案例 01&#xff1a;部署使用 eNSP 平台实验需求&#xff1a; 安装华为 eNSP 网络模拟平台打开 eNSP 平台&#xff0c;新建拓扑并绘制网络能够成功启动交换机、计算机设备 实验步骤&#xff1a; 安装华为 eNSP 网络模拟平台启动安装程序 配置安装内容 防护墙允许 eNSP 程序的…

IDTL:茶叶病害识别数据集(猫脸码客 第205期)

Identifying Disease in Tea Leaves茶叶病害识别数据集 一、引言 在农业领域&#xff0c;茶叶作为一种重要的经济作物&#xff0c;其生产过程中的病害防治是确保茶叶质量和产量的关键环节。然而&#xff0c;传统的病害识别方法主要依赖于人工观察和经验判断&#xff0c;这不仅…

从零开始实现RPC框架---------项目介绍及环境准备

一&#xff0c;介绍 RPC&#xff08;Remote Procedure Call&#xff09;远程过程调⽤&#xff0c;是⼀种通过⽹络从远程计算机上请求服务&#xff0c;⽽不需要 了解底层⽹络通信细节。RPC可以使⽤多种⽹络协议进⾏通信&#xff0c; 如HTTP、TCP、UDP等&#xff0c; 并且在 TCP/…

匿名方法与Lambda表达式+泛型委托

匿名方法 和委托搭配使用&#xff0c;方便我们快速对委托进行传参&#xff0c;不需要我们定义一个新的函数&#xff0c;直接用delegate关键字代替方法名&#xff0c;后面跟上参数列表与方法体。 格式&#xff1a;delegate(参数列表){方法体} lambda表达式 是匿名方法的升级…

Brave编译指南2024 MacOS篇-环境配置(四)

引言 在上一篇文章中&#xff0c;我们成功获取了Brave浏览器的源代码。现在&#xff0c;我们将进入编译过程的关键阶段&#xff1a;环境配置。正确的环境配置对于成功编译Brave浏览器至关重要&#xff0c;它能确保所有必要的工具和依赖项都已就位&#xff0c;并且版本兼容。 …

JAVAIDEA初始工程的创建

四结构 建工程综述* 初始*&#xff1a; 1、先建个空项目&#xff0c; 2、打开文件中的项目结构新建module模块&#xff08;模块下有src&#xff09; 修改模块名&#xff1a; 也是Refactor&#xff0c;Rename&#xff0c;但是要选第三个同时改模块和文件夹名字 导入模块&am…

【Python】ftfy 使用指南:修复 Unicode 编码问题

ftfy&#xff08;fixes text for you&#xff09;是一个专为修复各种文本编码错误而设计的 Python 工具。它的主要目标是将损坏的 Unicode 文本恢复为正确的 Unicode 格式。ftfy 并非用于处理非 Unicode 编码&#xff0c;而是旨在修复因为编码不一致、解码错误或混合编码导致的…

【Python】path:简化文件路径处理的 Python 库

path 是一个 Python 库&#xff0c;提供了对文件系统路径的简洁抽象&#xff0c;使文件和目录操作更加直观和 Pythonic。该库建立在 pathlib 的基础上&#xff0c;扩展了文件路径处理的功能&#xff0c;使得开发者能够更高效地进行文件操作&#xff0c;如文件读写、目录遍历、路…

Redis缓存穿透雪崩击穿及解决

封装缓存空对象解决缓存穿透与逻辑过期解决缓存击穿工具类 Slf4j Component public class CacheClient {private final StringRedisTemplate stringRedisTemplate;public CacheClient(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate stringRedisTemplat…

《Linux从小白到高手》理论篇(十一):Linux的系统环境管理

值此国庆佳节&#xff0c;深宅家中&#xff0c;闲来无事&#xff0c;就多写几篇博文。本篇详细深入介绍Linux的系统环境管理。 环境变量 linux系统下&#xff0c;如果你下载并安装了应用程序&#xff0c;很有可能在键入它的名称时出现“command not found”的提示内容。如果每…

震撼!AI造声新标杆,20字生成完美音频

震撼&#xff01;AI造声新标杆&#xff0c;20字生成完美音频 EzAudio是一款革命性的文本到音频生成AI&#x1f3b6;&#xff0c;快速生成高质量音频&#xff0c;告别机械音&#x1f50a;。它能将文字瞬间变成音乐和配音&#xff0c;为创作增添无限可能✨&#xff01;快来体验这…

源2.0全面适配百度PaddleNLP,大模型开发开箱即用

近日&#xff0c;源2.0开源大模型与百度PaddleNLP完成全面适配。用户通过PaddleNLP&#xff0c;可快速调用源2.0预训练大模型&#xff0c;使用源2.0在语义、数学、推理、代码、知识等方面的推理能力&#xff0c;也可以使用特定领域的数据集对源2.0 进行微调&#xff0c;训练出适…

C++11_lambda

lambda表达式 在C98中&#xff0c;如果想要对一个数据集合中的元素进行排序&#xff0c;可以使用std::sort方法。比如说&#xff0c;我想在某宝买一件商品&#xff0c;我想买该商品价格最便宜的哪一个&#xff0c;这就需要对价格排升序&#xff1b;我如果想买性价比最高的&…

AAC-Fe³⁺水凝胶,兼具拉伸性与导电性,还有自修复和4D打印能力

大家好&#xff01;今天我们来了解一种用于可拉伸电子产品的创新材料——自修复和4D打印水凝胶——《Self‐Healable and 4D Printable Hydrogel for Stretchable Electronics》发表于《Advanced Science》。在科技发展中&#xff0c;可拉伸电子产品需求大增&#xff0c;但现有…

我尝试了LangGraph Studio的AI Agent功能

构建一个真正“智能”的Agent——一个能够理解语言、做出决策并进行有意义互动的Agent——并不像编写几行代码那么简单。 它需要对AI原理和软件工程有深刻的理解。 此外&#xff0c;传统的软件工具并不适合Agent的开发&#xff0c;无法满足其独特需求。 这也是像LangGraph S…

【数据分享】2001-2023年我国省市县镇四级的逐月平均气温数据(免费获取/Shp/Excel格式)

之前我们分享过1901-2023年1km分辨率逐月平均气温栅格数据&#xff0c;该数据来源于国家青藏高原科学数据中心。为方便大家使用&#xff0c;我们还基于上述平均气温栅格数据将数据处理为Shp和Excel格式的省市县三级逐月平均气温数据&#xff08;可查看之前的文章获悉详情&#…

10.2今日错题解析(软考)

目录 前言面向对象技术——设计模式的应用场景系统开发基础——概要设计与详细设计 前言 这是用来记录我备考软考设计师的错题的&#xff0c;今天知识点为设计模式的应用场景、概要设计与详细设计&#xff0c;大部分错题摘自希赛中的题目&#xff0c;但相关解析是原创&#xf…

银河麒麟V10如何配置外网yum源?

银河麒麟V10如何配置外网yum源&#xff1f; 一、常用的软件源地址二、配置yum源的步骤1. 打开终端2. 进入yum配置文件目录3. 编辑或创建.repo文件4. 配置软件源信息5. 保存并退出6. 更新软件包列表7. 验证软件源 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不…

三色标记算法

三色标记算法 GC---> 标记&#xff08;可达性算法&#xff09;---> 根据不同算法去处理回收 STW&#xff1a;GC时对程序暂停处理下垃圾。不暂停&#xff0c;就会一直制造垃圾&#xff0c;清理不干净。暂停就会阻塞期间请求&#xff0c;影响系统性能 三色标记&#xff…

媒介坊:软文自助发布平台,开启营销新篇章

在数字化时代&#xff0c;企业营销的方式日新月异&#xff0c;软文作为一种高效、低成本的营销手段&#xff0c;越来越受到企业的青睐。然而&#xff0c;如何在众多媒体中精准投放软文&#xff0c;实现品牌的有效传播&#xff0c;成为了众多企业关注的焦点。媒介坊软文自助发布…