C++模拟实现AVL树

news2025/4/22 2:53:03

目录

1.文章概括

2.AVL树概念

3.AVL树的性质

4.AVL树的插入

5.旋转控制

1.左单旋

2. 右单旋

3.左右双旋

4.右左双旋

6.全部代码


1.文章概括

        本文适合理解平衡二叉树的读者阅读,因为AVL树是平衡二叉树的一种优化,其大部分实现逻辑与平衡二叉树是相同的,相同的部分不做过多阐述。

2.AVL树概念

        平衡二叉树主要用于查找数据,可提高查找效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。在这样的缺点下,AVL树被发明了出来。AVL树的优化点在于:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

3.AVL树的性质

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

1.它的左右子树都是AVL树;

2.左右子树高度之差(简称平衡因子)的绝对值不超过1(-1,0,1)。

AVL树 == 高度平衡二叉搜索树,说是平衡,为什么不是相等?

因为高度差不超过1,不是高度相等。

AVL树图片示例:

        如此控制后,增删改查的时间复杂度即为高度次 == O(logN)。

4.AVL树的插入

        对于一个结点的插入,分析如下:

1.新增在该结点的左,parent平衡因子减减;

2.新增在该结点的右,parent平衡因子加加;

3.更新后parent平衡因子 == 0,说明parent所在的子树的高度不变,不会再影响祖先,不用再沿着到root的路径往上更新;

4.更新后parent平衡因子 == 1 or -1,说明parent所在的子树的高度变化,会再影响祖先,需要沿着到root的路径往上更新;

5.更新后parent平衡因子 == 2 or -2,说明parent所在的子树的高度变化且不平衡,对parent所在子树进行旋转,让它平衡;

6.更到根结点。

补充说明:执行到4时会从1,2,开始继续循环,执行到3,5,6时插入结束。

旋转时需要注意的问题:

1.保持它是搜索树;

2.变成平衡树且降低这个子树的高度。

代码:

bool Insert(const T& data)
	{
		Node* parent = nullptr;
		Node* cur = _pRoot;
		if (cur == nullptr)
		{
			cur = new Node(data);
			_pRoot = cur;
		}
		while (cur)
		{
			if (cur->_data > data)
			{
				parent = cur;
				cur = cur->_pLeft;
			}
			else if (cur->_data < data)
			{
				parent = cur;
				cur = cur->_pRight;
			}
			else
				return false;
		}
		cur = new Node(data);
		if (parent->_data > data)
		{
			parent->_pLeft = cur;
			cur->_pParent = parent;
		}
		else
		{
			parent->_pRight = cur;
			cur->_pParent = parent;
		}
		while (parent)
		{
			if (parent->_pLeft == cur)
				parent->_bf--;
			else
				parent->_bf++;
			if (parent->_bf == 0)
				return true;
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				cur = parent;
				parent = parent->_pParent;
			}
			else
			{
				if (cur->_bf == 1 && parent->_bf == 2)
				{
					RotateL(parent);
				}
				else if (cur->_bf == -1 && parent->_bf == -2)
				{
					RotateR(parent);
				}
				else if (cur->_bf == -1 && parent->_bf == 2)
				{
					RotateRL(parent);
				}
				else if (cur->_bf == 1 && parent->_bf == -2)
				{
					RotateLR(parent);
				}
				break;
			}
		}
	}

        代码中,我使用了平衡因子来控制这棵树的高度。

5.旋转控制

1.左单旋

新节点插入较高右子树的右侧 --- 右右:

2. 右单旋

新节点插入较高左子树的左侧 --- 左左

3.左右双旋

 新节点插入较高左子树的右侧---左右:

4.右左双旋

新节点插入较高右子树的左侧 --- 右左:

6.全部代码

#pragma once
#include <iostream>
#include <assert.h>
#include <vector>
using namespace std;

template<class T>
struct AVLTreeNode
{
	AVLTreeNode(const T& data = T())
		: _pLeft(nullptr)
		, _pRight(nullptr)
		, _pParent(nullptr)
		, _data(data)
		, _bf(0)
	{}

	AVLTreeNode<T>* _pLeft;
	AVLTreeNode<T>* _pRight;
	AVLTreeNode<T>* _pParent;
	T _data;
	int _bf;   // 节点的平衡因子
};


// AVL: 二叉搜索树 + 平衡因子的限制
template<class T>
class AVLTree
{
	typedef AVLTreeNode<T> Node;
public:
	AVLTree()
		: _pRoot(nullptr)
	{}

	// 在AVL树中插入值为data的节点
	bool Insert(const T& data)
	{
		Node* parent = nullptr;
		Node* cur = _pRoot;
		if (cur == nullptr)
		{
			cur = new Node(data);
			_pRoot = cur;
		}
		while (cur)
		{
			if (cur->_data > data)
			{
				parent = cur;
				cur = cur->_pLeft;
			}
			else if (cur->_data < data)
			{
				parent = cur;
				cur = cur->_pRight;
			}
			else
				return false;
		}
		cur = new Node(data);
		if (parent->_data > data)
		{
			parent->_pLeft = cur;
			cur->_pParent = parent;
		}
		else
		{
			parent->_pRight = cur;
			cur->_pParent = parent;
		}
		while (parent)
		{
			if (parent->_pLeft == cur)
				parent->_bf--;
			else
				parent->_bf++;
			if (parent->_bf == 0)
				return true;
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				cur = parent;
				parent = parent->_pParent;
			}
			else
			{
				if (cur->_bf == 1 && parent->_bf == 2)
				{
					RotateL(parent);
				}
				else if (cur->_bf == -1 && parent->_bf == -2)
				{
					RotateR(parent);
				}
				else if (cur->_bf == -1 && parent->_bf == 2)
				{
					RotateRL(parent);
				}
				else if (cur->_bf == 1 && parent->_bf == -2)
				{
					RotateLR(parent);
				}
				break;
			}
		}
	}

	// AVL树的验证
	bool IsAVLTree()
	{
		return _IsAVLTree(_pRoot);
	}

private:
	// 根据AVL树的概念验证pRoot是否为有效的AVL树
	bool _IsAVLTree(Node* root)
	{
		if (root == nullptr)
			return true;

		int leftHight = Height(root->_pLeft);
		int rightHight = Height(root->_pRight);

		if (rightHight - leftHight != root->_bf)
		{
			cout << "平衡因子异常:" << root->_data << "->" << root->_bf << endl;
			return false;
		}

		return abs(rightHight - leftHight) < 2
			&& _IsAVLTree(root->_pLeft)
			&& _IsAVLTree(root->_pRight);
	}

	size_t Height(Node* root)
	{
		if (root == nullptr)
			return 0;

		int leftHeight = Height(root->_pLeft);
		int rightHeight = Height(root->_pRight);

		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}
	// 右单旋
	void RotateR(Node* pParent) 
	{
		Node* cur = pParent->_pLeft;
		Node* curRight = cur->_pRight;
		Node* parentP = pParent->_pParent;

		pParent->_pLeft = curRight;
		if(curRight)
			curRight->_pParent = pParent;
		cur->_pRight = pParent;
		pParent->_pParent = cur;

		if (parentP == nullptr)
		{
			_pRoot = cur;
			cur->_pParent = nullptr;
		}
		else
		{
			cur->_pParent = parentP;
			if (parentP->_pLeft == pParent)
				parentP->_pLeft = cur;
			else
				parentP->_pRight = cur;
		}
		if(curRight)
			curRight->_bf = 0;
		pParent->_bf = cur->_bf = 0;
	}
	// 左单旋
	void RotateL(Node* pParent)
	{
		Node* cur = pParent->_pRight;
		Node* curLeft = cur->_pLeft;
		Node* parentP = pParent->_pParent;

		pParent->_pRight = curLeft;
		if(curLeft)
			curLeft->_pParent = pParent;
		cur->_pLeft = pParent;
		pParent->_pParent = cur;

		if (parentP == nullptr)
		{
			_pRoot = cur;
			cur->_pParent = nullptr;
		}
		else
		{
			cur->_pParent = parentP;
			if (parentP->_pLeft == pParent)
				parentP->_pLeft = cur;
			else
				parentP->_pRight = cur;
		}
		if(curLeft)
			curLeft->_bf = 0;
		pParent->_bf = cur->_bf = 0;
	}
	// 右左双旋
	void RotateRL(Node* pParent)
	{
		Node* cur = pParent->_pRight;
		Node* curLeft = cur->_pLeft;
		int temp = curLeft->_bf;
		RotateR(cur);
		RotateL(pParent);
		if (temp == 0)
			return;
		else if (temp == 1)
		{
			pParent->_bf = -1;
			cur->_bf = 0;
			curLeft->_bf = 0;
		}
		else if (temp == -1)
		{
			pParent->_bf = 0;
			cur->_bf = 1;
			curLeft->_bf = 0;
		}
		else
			assert(false);
	}
	// 左右双旋
	void RotateLR(Node* pParent)
	{
		Node* cur = pParent->_pLeft;
		Node* curRight = cur->_pRight;
		int temp = curRight->_bf;
		RotateL(cur);
		RotateR(pParent);
		if (temp == 0)
			return;
		else if (temp == 1)
		{
			pParent->_bf = 0;
			cur->_bf = -1;
			curRight->_bf = 0;
		}
		else if (temp == -1)
		{
			pParent->_bf = 1;
			cur->_bf = 0;
			curRight->_bf = 0;
		}
		else
			assert(false);
	}

private:
	Node* _pRoot;
};

  

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

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

相关文章

python卷积神经网络人脸识别示例实现详解

目录 一、准备 1&#xff09;使用pytorch 2&#xff09;安装pytorch 3&#xff09;准备训练和测试资源 二、卷积神经网络的基本结构 三、代码实现 1&#xff09;导入库 2&#xff09;数据预处理 3&#xff09;加载数据 4&#xff09;构建一个卷积神经网络 5&#xff0…

以Unity6.0为例,如何在Unity中开启DLSS功能

DLSS DLSS&#xff08;NVIDIA 深度学习超级采样&#xff09;&#xff1a;NVIDIA DLSS 是一套由 GeForce RTX™ Tensor Core 提供支持的神经渲染技术&#xff0c;可提高帧率&#xff0c;同时提供可与原生分辨率相媲美的清晰、高质量图像。目前最新突破DLSS 4 带来了新的多帧…

CSDN 大模型 笔记

AI 3大范式&#xff1a;计算 发发 交互 L1 生成代码 复制到IDEA &#xff08;22年12-23年6&#xff0c;7月份&#xff09; L2 部分自动编程 定义class 设计interface 让其填充实现 (23年7&#xff0c;8月份) L3 通用任务 CRUD (24年) L4 高度自动编程 通用领域专有任务&#xf…

Stability AI 联合 UIUC 提出单视图 3D 重建方法SPAR3D,可0.7秒完成重建并支持交互式用户编辑。

Stability AI 联合 UIUC 提出一种简单而有效的单视图 3D 重建方法 SPAR3D&#xff0c;这是一款最先进的 3D 重建器&#xff0c;可以从单视图图像重建高质量的 3D 网格。SPAR3D 的重建速度很快&#xff0c;只需 0.7 秒&#xff0c;并支持交互式用户编辑。 相关链接 论文&#xf…

网易易盾接入DeepSeek,数字内容安全“智”理能力全面升级

今年农历新年期间&#xff0c;全球AI领域再度掀起了一波革命性浪潮&#xff0c;国产通用大模型DeepSeek凭借其强大的多场景理解与内容生成能力迅速“出圈”&#xff0c;彻底改写全球人工智能产业的格局。 作为国内领先的数字内容风控服务商&#xff0c;网易易盾一直致力于探索…

自动驾驶---如何打造一款属于自己的自动驾驶系统

在笔者的专栏《自动驾驶Planning决策规划》中&#xff0c;主要讲解了行车的相关知识&#xff0c;从Routing&#xff0c;到Behavior Planning&#xff0c;再到Motion Planning&#xff0c;以及最后的Control&#xff0c;笔者都做了相关介绍&#xff0c;其中主要包括算法在量产上…

聚焦 AUTO TECH China 2025,共探汽车内外饰新未来Automotive Interiors

全球汽车产业蓬勃发展的大背景下&#xff0c;汽车内外饰作为汽车重要组成部分&#xff0c;其市场需求与技术创新不断推动着行业变革。2025年11月20日至22日&#xff0c;一场备受瞩目的行业盛会 ——AUTO TECH China 2025 广州国际汽车内外饰技术展览会将在广州保利世贸博览馆盛…

Moretl 增量文件采集工具

永久免费: <下载> <使用说明> 用途 定时全量或增量采集工控机,电脑文件或日志. 优势 开箱即用: 解压直接运行.不需额外下载.管理设备: 后台统一管理客户端.无人值守: 客户端自启动,自更新.稳定安全: 架构简单,兼容性好,通过授权控制访问. 架构 技术架构: Asp…

支持多种网络数据库格式的自动化转换工具——VisualXML

一、VisualXML软件介绍 对于DBC、ARXML……文件的编辑、修改等繁琐操作&#xff0c;WINDHILL风丘科技开发的总线设计工具——VisualXML&#xff0c;可轻松解决这一问题&#xff0c;提升工作效率。 VisualXML是一个强大且基于Excel表格生成多种网络数据库文件的转换工具&#…

四、OSG学习笔记-基础图元

前一章节&#xff1a; 三、OSG学习笔记-应用基础-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/145514021 代码&#xff1a;CuiQingCheng/OsgStudy - Gitee.com 一、绘制盒子模型 下面一个简单的 demo #include<windows.h> #include<osg/Node&…

window 安装GitLab服务器笔记

目录 视频&#xff1a; 资源&#xff1a; Linux CeneOS7&#xff1a; VMware&#xff1a; Linux无法安装 yum install vim -y 1.手动创建目录 2.下载repo PS 补充视频不可复制的代码 安装GitLab *修改root用户密码相关&#xff08;我卡在第一步就直接放弃了这个操作&…

MySQL数据库入门到大蛇尚硅谷宋红康老师笔记 基础篇 part 10

第10章_创建和管理表 DDL&#xff1a;数据定义语言。CREATE \ALTER\ DROP \RENAME TRUNCATE DML&#xff1a;数据操作语言。INSERT \DELETE \UPDATE \SELECT&#xff08;重中之重&#xff09; DCL&#xff1a;数据控制语言。COMMIT \…

前端如何判断浏览器 AdBlock/AdBlock Plus(最新版)广告屏蔽插件已开启拦截

2个月前AdBlock/AdBlock Plus疑似升级了一次 因为自己主要负责面对海外的用户项目&#xff0c;发现以前的检测AdBlock/AdBlock Plus开启状态方法已失效了&#xff0c;于是专门研究了一下。并尝试了很多方法。 已失效的老方法 // 定义一个检测 AdBlock 的函数 function chec…

html文件怎么转换成pdf文件,2025最新教程

将HTML文件转换成PDF文件&#xff0c;可以采取以下几种方法&#xff1a; 一、使用浏览器内置功能 打开HTML文件&#xff1a;在Chrome、Firefox、IE等浏览器中打开需要转换的HTML文件。打印对话框&#xff1a;按下CtrlP&#xff08;Windows&#xff09;或CommandP&#xff08;M…

科技查新过不了怎么办

“科技查新过不了怎么办&#xff1f;” “科技查新不通过的原因是什么&#xff1f;” 想必这些问题一直困扰着各位科研和学术的朋友们&#xff0c;尤其是对于查新经验不够多的小伙伴&#xff0c;在历经千难万险&#xff0c;从选择查新机构、填写线上委托单到付费&#xff0c;…

超详细的数据结构3(初阶C语言版)栈和队列。

文章目录 栈和队列1.栈1.1 概念与结构1.2 栈的实现 2. 队列2.1 概念与结构2.2 队列的实现 总结 栈和队列 1.栈 1.1 概念与结构 栈&#xff1a;⼀种特殊的线性表&#xff0c;其只允许在固定的⼀端进行插⼊和删除元素操作。进⾏数据插⼊和删除操作的⼀端称为栈顶&#xff0c;另…

centos 7 关于引用stdatomic.h的问题

问题&#xff1a;/tmp/tmp4usxmdso/main.c:6:23: fatal error: stdatomic.h: No such file or directory #include <stdatomic.h> 解决步骤&#xff1a; 1.这个错误是因为缺少C编译器的标准原子操作头文件 stdatomic.h。在Linux系统中&#xff0c;我们需要安装开发工具…

Unity WebGL包体压缩

最近在开发webgl&#xff0c;踩了很多坑&#xff0c;先来说下包体的问题。 开发完之后发现unity将文件都合并到一个文件了&#xff0c;一共有接近100m。 这对网页端的体验来说是可怕的&#xff0c;因为玩家必须要加载完所有的文件才能进入&#xff0c;这样体验特别差。 于是想…

【对比测评】 .NET 应用的 Web 视图控件:DotNetBrowser 或 EO.WebBrowser

您是否需要 .NET 应用的 Web 视图控件&#xff1f;.NET 生态系统提供了很多东西&#xff0c;有免费的 Web 视图控件&#xff0c;既有开源的&#xff0c;也有专有的。还有一些商业 Web 视图 控件&#xff0c;也是企业经常选择的一种选项。 在这篇博文中&#xff0c;我们比较了商…

Redis 数据类型 String 字符串

Redis 中的 String 数据类型 是最基础且使用最广泛的数据类型之一。它本质上是一个字节序列&#xff0c;可以存储各种类型的数据&#xff0c;如字符串、整数、浮点数等&#xff0c;其字符串类型的值包含⼀般格式的字符串或者类似 JSON、XML 格式的字符串&#xff1b;还可以存储…