数据结构---AVL树

news2024/9/26 5:21:57

AVL树

  • AVL树的概念
  • AVL树节点的定义
  • AVL树的插入
  • 源代码

AVL树的概念

二叉搜索树虽然可以缩短查找的效率,但是,如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率就会变低。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

一颗AVL树或者是空树,或者是具有以下性质的二叉搜索树。

  • 它的左右子树都是AVL树
  • 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

在这里插入图片描述

如果一颗二叉搜索树是高度平衡的,他就是AVL树。如果他有n个皆点,其高度可保持在 O(log2n),搜索时间复杂度O(log2n)。

AVL树节点的定义

template <class K, class V>
class AVLNode
{
public:
	AVLNode<K, V>* _letf;
	AVLNode<K, V>* _right;
	AVLNode<K, V>* _parent;

	pair<K, V> _kv;

	int _bf;

	AVLNode(const pair<K, V>& kv)
		:_left(nullptr)// 左孩子
		, _right(nullptr)// 右孩子
		, _parent(nullptr)// 父节点
		, _kv(kv)// 平衡因子
		,_bf(0)
	{}

};

AVL树的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索申诉,那么AVL树的插入过程可以分为两步。

  1. 按照二叉搜索树的方式插入新节点
  2. 调整平衡因子

如果这个节点新增在左,parent平衡因子减1;如果新增在右,parent平衡因子加1。如果更新后parent平衡因子为0,说明parent所在的子树的高度不变,不会影响祖先,不用再继续沿着到root的路径往上更新。更新后parent的平衡因子为0或者-1,说明parent所在的子树的高度变化,会影响祖先,需要沿着到root的路径往上更新。更新后parent平衡因子为2或者-2,说明parent所在子树的高度变化且不平衡,要对parent所在的子树进行旋转,让它平衡。

如何通过旋转,让这个树平衡呢?

右边高,就往左边旋转。

左边高,就往右边旋转。

旋转的时候要注意,保持他是搜索树,变成平衡树且降低这个子树的高度。
在这里插入图片描述

左单旋,右边高,强行往左边旋。

右单旋与左单旋道理一样。


在这里插入图片描述

如果出现了上图中的树,靠左单旋或者右单旋就不能到平衡的目的了。这种情况要靠双旋来解决。

就图中的树而言:先以7为旋转点,进行右单旋(此时6会变成7的右儿子,此时树的情况就是一条斜线),在以5为旋转点,进行左单旋。就可以平衡了。

但这个时候需要对平衡因子做出调整,因为左右旋转之后会把平衡因子更新为0,所以到强行更改一下旋转部分的平衡因子。

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

		Node* cur = _root;
		Node* parent = nullptr;
		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 if (cur == parent->_parent)
			{
				parent->_bf++;
			}

			if (parent->_bf == 0)
			{
				// 再更新的过程中,父节点的平衡因子=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);
				}// 1.左单旋
				else if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateR(parent);
				}// 2.右单旋
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
				}// 3.右左单旋
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}// 4.左右单旋
				break;
			}
			else
			{
				// 防止代码出bug,遇到平衡因子为>2的情况。
				assert(false);
			}

		}

		return true;

	}

源代码

#pragma once

template <class K, class V>
class AVLNode
{
public:
	AVLNode<K, V>* _left;
	AVLNode<K, V>* _right;
	AVLNode<K, V>* _parent;

	pair<K, V> _kv;

	int _bf;

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

};


template <class K, class V>
class AVLTree
{
public:
	typedef AVLNode<K, V> Node;

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

		Node* cur = _root;
		Node* parent = nullptr;
		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 if (cur == parent->_parent)
			{
				parent->_bf++;
			}

			if (parent->_bf == 0)
			{
				// 再更新的过程中,父节点的平衡因子=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);
				}// 1.左单旋
				else if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateR(parent);
				}// 2.右单旋
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
				}// 3.右左单旋
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}// 4.左右单旋
				break;
			}
			else
			{
				// 防止代码出bug,遇到平衡因子为>2的情况。
				assert(false);
			}

		}

		return true;

	}

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



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

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

	}


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


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

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

	void RotateL(Node* parent)
	{
		Node* cur = parent->_right;
		Node* curleft = cur->_left;
		
		parent->_right = curleft;
		if (curleft)
			curleft->_parent = parent;

		cur->_left = parent;
		Node* ppnode = parent->_parent; // 如果parent是一颗局部子树,ppnode用来记录局部子树的父节点
		parent->_parent = cur;

		if (parent == _root) // 如果parent是根节点
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else // 如果parent是一颗局部的子树
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
			}
			else
			{
				ppnode->_right = cur;
			}
			cur->_parent = ppnode;
		}


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

	void RotateR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curright = cur->_right;

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


		cur->_right = parent;
		Node* ppnode = parent->_parent;
		parent->_parent = cur;

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

			}

			cur->_parent = ppnode;
		}

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

	void InOrder()
	{
		_InOrder(_root);
	}

	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOrder(root->_left);
		cout << root->_kv.first << endl;
		_InOrder(root->_right);
	}

private:
	Node* _root = nullptr;
};

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

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

相关文章

Java移除链表元素

目录 1.题目描述 2.题解 题解1 题解2 1.题目描述 给你一个链表的头节点 head 和一个整数 val&#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 输入&#xff1a;head [1,2,6,3,4,5,6]&#xff0c;val 6 输出&#xff1a;…

饥饿游戏搜索算法(HGS)(含java实现代码)

Hunger games search: Visions, conception, implementation, deep analysis, perspectives, and towards performance shifts 期刊:Expert Systems With Applications SCI1区 主体框架 public HGS(){initialize();calculateFitness();sortTheFitness();calculateHungry();for…

分享团队在软件开发中用到的神仙工具

目前使用的是JNPF框架。 技术栈上使用的SpringBoot、SpringCloud、Spring Web、MyBatis、Swagger、Vue、Element。 这些都是比较主流的技术&#xff0c;无论是技术层面的先进性还是学习难度都是比较低的&#xff0c;目前网络上有大量可供参考学习的资料。 并且它支持前后端分离…

中文转拼音(带音调)

导入maven依赖 <!--导入pinyin4j库--><dependency><groupId>com.belerweb</groupId><artifactId>pinyin4j</artifactId><version>2.5.1</version></dependency>demo如下&#xff1a; import com.github.stuxuhai.jpiny…

方案:数智化视频AI技术为智慧防汛筑基,构建防汛“数字堤坝”

一、背景分析 在过去的几年中&#xff0c;全球气候变化导致许多城市在雨季面临严重的洪涝灾害。这些灾害不仅对人们的生命安全和财产造成威胁&#xff0c;也影响了城市的正常运转。传统的防汛手段主要依赖人力监控和应急指挥&#xff0c;但存在响应速度慢、处理效率低等问题。…

【操作系统】进程控制与进程通信

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaEE 操作系统 Redis 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 操作系统 一、进程控制1.1 什么是进程控制1…

vue element 搜索框根据后台的接口实现模糊查询 + 分页特殊处理+重置表格

模糊查询效果图 1.配置接口 search: "/api/goods/search", //搜索接口/goods/search 2.get接口 search(params) { return axios.get(base.search, { params });//后台传参 再写这个params }, 3.异步请求接口 // 搜索接口async search(search){let res await this…

基于Java社区生鲜电商平台设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

离子风刀的特点以及应用领域

静电消除器离子风刀是一种用于消除静电的设备&#xff0c;它通过在空气中产生离子&#xff0c;将静电从物体表面或人体上释放掉&#xff0c;以保护电子设备和人体免受静电的损害。 离子风刀的特点包括&#xff1a; 1、高效&#xff1a;离子风刀能够快速消除静电&#xff0c;提…

CRM软件系统趣味性——游戏化销售管理

对于企业销售来说&#xff0c;高薪酬也伴随着更高的压力与挑战。高强度的单一工作会让销售人员逐渐失去对工作的兴趣&#xff0c;导致售状态缺少动力和激情&#xff0c;工作开展愈加困难。您可以通过CRM系统进行游戏化销售管理&#xff0c;让销售人员重新干劲满满。 游戏并不是…

SkyWalking使用讲解

文章目录 1 SkyWalking1.1 简介1.2 如何选择1.3 Skywalking架构1.4 服务端搭建1.4.1 下载安装包1.4.2 配置修改1.4.2.1 config/application.yml1.4.2.2 webapp/webapp.yml 1.4.3 启动服务 1.5 客户端搭建1.6 数据持久化1.6.1 修改配置文件1.6.2 添加MySQL的jdbc依赖 1.7 日志监…

Java面经整理(1)

一)Java中支持多继承吗,为什么? 答案:在JAVA中是不支持多继承的,原因是多继承会存在菱形继承的问题 菱形继承: 1)菱形继承也被称之为是钻石继承,是一种在JAVA面向对象编程的时候遇到的一个可能出现的继承问题; 2)假设JAVA支持多继承,那么就有可能一个类D继承两个不同的类…

一文总结提示工程框架,除了CoT还有ToT、GoT、AoT、SoT、PoT

夕小瑶科技说 原创 编译 | 谢年年 大语言模型LLM被视为一个巨大的知识库&#xff0c;它可以根据你提出问题或陈述的方式来提供答案。就像人类可能会根据问题的不同提供不同的答案一样&#xff0c;LLM也可以根据输入的不同给出不同的答案。因此&#xff0c;你的问题或陈述方式就…

视频太大怎么压缩变小?把视频变小这样做

随着科技的不断发展&#xff0c;视频已经成为了我们日常生活中不可或缺的一部分&#xff0c;然而&#xff0c;有时候我们会遇到视频体积太大&#xff0c;无法上传或者传输的问题&#xff0c;那么&#xff0c;如何将过大的视频压缩变小呢&#xff1f;下面就给大家分享几个方法&a…

如何检测出鸡蛋壳上的裂缝(个人想法,正确性有待研究)

问题 老师在课上提出了一个项目&#xff0c;是关于如何通过某些方式来找出有裂缝的鸡蛋壳&#xff0c;但是鸡蛋壳上的裂缝非常小&#xff0c;问有什么办法处理。 想法 通过瞬间增大气压使得有裂缝的鸡蛋破裂。 具体实施 在图中&#xff0c;我们可以看到鸡蛋受外界气压的力&…

TS自动监视ts文件修改

当我们对ts文件进行编译后会生成js文件 当我们在ts文件中进行修改时&#xff0c;js文件并不会进行变化 那我们该如何监视ts文件中的变化呢&#xff1f;可以在控制台中输入如下命令&#xff1a; tsc 文件名.ts -w 这下在ts中代码改变就可以被监听&#xff08;这里报错是因为同时…

【UML】类图详解

UML UML ——Unified modeling language UML&#xff08;统一建模语言&#xff09;&#xff0c;是一种用于软件系统分析和设计的语言工具&#xff0c;它用
于帮助软件开发人员进行思考和记录思路的结果 UML图有哪些 用例图静态结构图∶类图、对象图、包图、组件图、部署图动…

Python 搭建编程环境

一、搭建编程环境 1、下载python 官网&#xff1a;https://www.python.org 2、开始安装 下载安装版本&#xff0c;双击下载的安装包&#xff0c;如下&#xff1a; 步骤一&#xff1a; 步骤二&#xff1a; 步骤三&#xff1a; 安装完成后执行下面的操作&#xff0c;判断是否…

[移动通讯]【Carrier Aggregation-4】【LTE-5】

前言&#xff1a; 前面讲过通过能力上报&#xff0c;以及RRC Connection Reconfiguration 添加SCell,添加完成后&#xff0c;UE 处于Inactive 状态。 本章主要讨论一下 Inactive 状态Active 状态的切换. 3GPP TS 36.321 V11.1.0 section 6.1.3.8 "Activation/Deactivat…

使用Python抢购商品

使用Python抢购商品 前言准备工作安装selenium库下载ChromeDriver 编写抢购商品py脚本导入库selenium使用方法 示例代码淘宝网华为商城 前言 注意&#xff1a;示例代码仅供学习使用&#xff0c;禁止不正当盈利。 本文使用Python的selenium库通过Chrome浏览器来抢购商品。首先…