【C++ AVL树】

news2024/9/22 21:29:29

文章目录

    • 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.coloradmin.cn/o/1484127.html

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

相关文章

Unity游戏输入系统(新版+旧版)

使用新版还是旧版 旧版 using System.Collections; using System.Collections.Generic; using UnityEngine;public class c5 : MonoBehaviour {void Start(){}void Update(){// 注意要在游戏中 点鼠标键盘进行测试// 鼠标// 0左键 1右键 2滚轮if (Input.GetMouseButtonDown(0)…

python爬虫之selenium知识点记录

selenium 一、前期准备 1、概述 selenium本身是一个自动化测试工具。它可以让python代码调用浏览器。并获取到浏览器中加载的各种资源。 我们可以利用selenium提供的各项功能。 帮助我们完成数据的抓取。 2、学习目标 掌握 selenium发送请求&#xff0c;加载网页的方法 掌…

新一代电话机器人开源PHP源代码

使用easyswoole 框架开发的 新一代电话机器人开源PHP源码 项目地址&#xff1a;https://gitee.com/ddrjcode/robotphp 代理商页面演示地址 http://119.23.229.15:8080 用户名&#xff1a;c0508 密码&#xff1a;123456 包含 AI外呼管理&#xff0c;话术管理&#xff0c;CR…

简易内存池2 - 华为OD统一考试(C卷)

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 200分 题解&#xff1a; Java / Python / C 题目描述 请实现一个简易内存池,根据请求命令完成内存分配和释放。 内存池支持两种操作命令&#xff0c;REQUEST和RELEASE&#xff0c;其格式为: REQUEST请求的内存大小 …

golang学习5,glang的web的restful接口

1. //返回json r.GET("/getJson", controller.GetUserInfo) package mainimport (/*"net/http"*/"gin/src/main/controller""github.com/gin-gonic/gin" )func main() {r : gin.Default()r.GET("/get", func(ctx *…

【研发日记】Matlab/Simulink技能解锁(三)——在Stateflow编辑窗口Debug

文章目录 前言 State断点 Transition断点 条件断点 按State步进 Watch Data Value Sequence Viewer 分析和应用 总结 前言 见《【研发日记】Matlab/Simulink技能解锁(一)——在Simulink编辑窗口Debug》 见《【研发日记】Matlab/Simulink技能解锁(二)——在Function编辑…

数据结构开篇

目录 一. 如何学好数据结构二. 基本概念和术语2.1 区分数据、数据元素、数据项、数据对象2.2 数据结构2.2.1 逻辑结构2.2.2 存储结构 2.3 数据类型和抽象数据类型2.4 抽象数据类型的实现 \quad 一. 如何学好数据结构 勤于思考;多做练习;多上机;善于寻求帮助;不怕困难&#xff…

NLP算法实战项目:使用 BERT 进行文本多分类

大多数研究人员将他们的研究论文提交给学术会议&#xff0c;因为这是更快地使研究结果可用的途径。寻找和选择合适的会议一直是一项具有挑战性的任务&#xff0c;特别是对于年轻的研究人员来说。基于先前会议论文集的数据&#xff0c;研究人员可以增加其论文被接受和发表的机会…

基于协同过滤算法的图书推荐系统

目录 一、功能介绍 二、开发环境 三、安装部署说明 一、功能介绍 本系统是一个采用协同过滤算法的图书推荐系统。 数据集&#xff1a;数据集来自亚马逊开源的Book-Crossings数据集。Book-Crossings数据集包含 278,858 个用户的 271,379 本书的 1,149,780 个评分。评分范围从1…

FL Studio选购指南:新手小白应该选择哪个版本FL Studio?

很多打算入手正版FL Studio的新手朋友都会纠结一个问题&#xff1a;哪个版本的FL Studio更适合我&#xff0c;到底应该入手哪一款FL Studio&#xff1f;本文会介绍每个版本之间的差异点&#xff0c;并带大家选择适合自己的FL Sudio版本。 FL Studio全版本 在选购前有一些小知识…

25高数考研张宇 -- 公式总结(更新中)

1. 两个重要极限 (1) lim ⁡ x → 0 sin ⁡ x x 1 \lim _{x \rightarrow 0} \frac{\sin x}{x}1 limx→0​xsinx​1, 推广形式 lim ⁡ f ( x ) → 0 sin ⁡ f ( x ) f ( x ) 1 \lim _{f(x) \rightarrow 0} \frac{\sin f(x)}{f(x)}1 limf(x)→0​f(x)sinf(x)​1. (2) lim ⁡…

小项目:2024/3/2

一、TCP机械臂测试 代码&#xff1a; #include <myhead.h> #define SER_IP "192.168.125.254" //服务器端IP #define SER_PORT 8888 //服务器端端口号#define CLI_IP "192.168.199.131" //客户端IP #define CLI_P…

Linux线程【互斥与同步】

目录 1.资源共享问题 1.1多线程并发访问 1.2临界区和临界资源 1.3互斥锁 2.多线程抢票 2.1并发抢票 2.2 引发问题 3.线程互斥 3.1互斥锁相关操作 3.1.1互斥锁创建与销毁 3.1.2、加锁操作 3.1.3 解锁操作 3.2.解决抢票问题 3.2.1互斥锁细节 3.3互斥…

[NSSCTF 2nd] web复现

1.php签到 <?phpfunction waf($filename){$black_list array("ph", "htaccess", "ini");$ext pathinfo($filename, PATHINFO_EXTENSION);foreach ($black_list as $value) {if (stristr($ext, $value)){return false;}}return true; }if(i…

【Spring IoC】实验四:特殊值处理

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

【NDK系列】Android tombstone文件分析

文件位置 data/tombstone/tombstone_xx.txt 获取tombstone文件命令&#xff1a; adb shell cp /data/tombstones ./tombstones 触发时机 NDK程序在发生崩溃时&#xff0c;它会在路径/data/tombstones/下产生导致程序crash的文件tombstone_xx&#xff0c;记录了死亡了进程的…

electron nsis 安装包 window下任务栏无法正常固定与取消固定 Pin to taskbar

问题 win10系统下&#xff0c;程序任务栏在固定后取消固定&#xff0c;展示的程序内容异常。 排查 1.通过论坛查询&#xff0c;应该是与app的api setAppUserModelId 相关 https://github.com/electron/electron/issues/3303 2.electron-builder脚本 electron-builder…

ABAP - SALV教程04 添加状态栏

CL_SALV_TABLE中提供了 GET_FUNCTIONS方法.GET_FUNCTIONS方法返回的是一个CL_SALV_FUNCTIONS_LIST类型的实例对象. 类CL_SALV_FUNCTIONS_LIST两个方法(SET_ALL、SET_DEFAULT)可以添加标准状态栏 实现步骤: 定义SET_STATUS私有方法. PRIVATE SECTION.METHODS:set_status CHA…

qt 基于百度API的人脸识别

百度云官网&#xff1a;点击跳转 一、创建应用 跳转进去&#xff0c;可以看到以下界面&#xff1a; 点击红色圈内的“去创建”&#xff0c;创建自己的项目。可以看到以下界面&#xff1a; 输入“应用名称”&#xff0c;并勾选“人脸对比”&#xff0c;还要到页面的最后输入“应…

董兆祥出席工业废水资源化,开创变废为宝新途径演讲

演讲嘉宾&#xff1a;董兆祥 董事长 河北奥博水处理有限公司 演讲题目&#xff1a;工业废水资源化&#xff0c;开创变废为宝新途径 会议简介 “十四五”规划中提出&#xff0c;提高工业、能源领城智能化与信息化融合&#xff0c;明确“低碳经济”新的战略目标&#xff0c;热…