C++ AVLTree

news2025/9/16 7:06:52

目录

介绍

节点的定义

AVLTree结构

Insert

插入节点

调节平衡因子 

void rotateR(node* parent)

void rotateL(node* parent)

void rotateRL(node* parent)

void rotateLR(node* parent)

 void InOrder()

 int height()

 int size()

 bool isBalance()


介绍

  • 如果是有序插入BSTree,高度就是n,那么查找的时间复杂度就是n;通过旋转,来调整高度就是AVLTree

节点的定义

template<class K, class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left; // <K, V> 要不要写
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	pair<K, V> _kv;
	int _bf;

	AVLTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_bf(0)
	{}
};
  • 类+模板才是类型

AVLTree结构

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> node;
public:

private:
	node* _root = nullptr;
};
  • 必须初始化_root;不然会影响Insert第一个节点的插入

Insert

插入节点

  • 要给 _root 赋值上第一个节点的地址
  • 通过parent节点来链接新的节点,parent节点初始化为cur就行,可以不初始化为nullptr,因为cur一定不是nullptr;所以不用担心后面parent会解引用空指针
bool Insert(pair<K, V> kv) // return
{
	if (_root == nullptr)
	{
		_root = new node(kv);
		return true;
	}
	node* cur = _root;
	node* parent = cur;
	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;
	//调节平衡因子
}

调节平衡因子 

  • 新增在右树,parent 的 bf++
  • 改变完parent,判断bf
  • bf == 0就直接跳出,调整完毕;因为0是由1或者-1变来的,变为0就是parent的左右树高度由一边高1变为相等(也就是矮的那边多了一个节点),自然不会影响pparent的高度
  • bf == 1 || bf == -1 说明parent的高度变了,那么pparent的高度也变了,所以需要往上更新pparent的bf
  • while的跳出的条件;cur->_parent:我的调整bf原因是子影响父,所以父不为空就调整;当然,条件也可以为parent
bool Insert(pair<K, V> kv) // return
{
    //插入...
    //调节bf
	while (cur->_parent)
	{
		if (parent->_right == cur)
		{
			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)
		{
			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);
		}
	}
}

void rotateR(node* parent)

操作 

  • 红圈是新增节点
  • 插入到左边,左边高了,为了降低parent这个子树,可以将parent变成subL的右子树,达到将高度的目的
  • 调整指向的顺序是先①在②,这样更有旋转的感觉;先②再①当然也可以,毕竟这4个节点都被定义了,都可以直接拿到
  • 对parent的讨论:是否为 _root
  • 一共调整4个节点,那么两两之间的父子链接操作就有3次

解释

  • 插在最左边这个树的左右都不影响,因为parent不连接这边
  • 最后就是改bf,parent和subL一定是0吗?parent的左右子树要么都为空,要么都只有一个节点;
  • 这个右旋的情况实际上只有两种;因为节点是一个个插入的,所以每一次插入都会判断是否旋转;你所看到的十几种情况的,我觉得都不准确,因为早就发生了旋转,他们所对应的那种树型根本就不会存在

void rotateR(node* parent)
{
	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 (parent == _root)
	{
		_root == subL;
	}
	else
	{
		if (pparent->_kv.first < subL->_kv.first)
		{
			pparent->_right = subL;
		}
		else
		{
			pparent->_left = subL;
		}
	}
	subL->_parent = pparent;
	parent->_bf = subL->_bf = 0;
}

void rotateL(node* parent)

操作 

  • 和左旋一模一样,可以理解为镜像反转一下
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 (parent == _root)
	{
		_root = subR;
	}
	else
	{
		if (pparent->_kv.first < subR->_kv.first)
		{
			pparent->_right = subR;
		}
		else
		{
			pparent->_left = subR;
		}
	}
	subR->_parent = pparent;

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

void rotateRL(node* parent)

操作 

  • 对平衡因子的调控,先右左旋,旋完之后bf都为0
  • 然后分情况调控bf

 解释

  • 对新插入的节点在subRL的左边和右边是不一样的
  • 插到左边 (即bf == -1),最后会给到parent,那么sub左边就会空缺,subR->_bf就要改成1
  • 插到右边 (即bf == 1) ,最后会给到subR,那么parent右边就会空缺,parent->_bf就要改成-1

  • 还有一种就是bf == 0(即subRL是新插入的节点)这时候parent和subR对应的左右子树都为空,一定为空,不然不会发生右左旋

void rotateRL(node* parent)
{
	node* subR = parent->_right;
	node* subRL = subR->_left;
	int bf = subRL->_bf;

	rotateR(subR);
	rotateL(parent);

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

void rotateLR(node* parent)

解释

  • 镜像对称,平衡因子反一下就行
void rotateLR(node* parent)
{
	node* subL = parent->_left;
	node* subLR = subL->_right;
	int bf = subLR->_bf;

	rotateL(subL);
	rotateR(parent);

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

 void InOrder()

public:
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
private:
	void _InOrder(node* root)
	{
		if (root == nullptr)
			return;

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

 int height()

public:
	int height()
	{
		return _height(_root);
	}
private:
	int _height(node* root)
	{
		if (root == nullptr)
			return 0;
		return max(_height(root->_left), _height(root->_right)) + 1;
	}

 int size()

public:
	int size()
	{
		return _size(_root);
	}
private:
	int _size(node* root)
	{
		if (root == nullptr)
			return 0;
		return _size(root->_left) + _size(root->_right) + 1;
	}

 bool isBalance()

 先序

public:
	bool isBalance()
	{
		return _isBalance(_root);
	}
private:
	bool _isBalance(node* root)
	{
		if (root == nullptr)
			return true;
		int leftHeight = _height(root->_left);
		int rightHeight = _height(root->_right);

		if (abs(leftHeight - rightHeight) >= 2)
			return false;

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

 后序

public:
	bool isBalanced()
	{
		int height = 0;
		return _isBalance(_root, height);
	}
private:
	bool _isBalance(node* root, int& height)
	{
		if (root == nullptr)
		{
			height = 0;
			return true;
		}

		int leftHeight = 0;
		int rightHeight = 0;

		bool isLeftBalanced = _isBalance(root->_left, leftHeight);
		bool isRightBalanced = _isBalance(root->_right, rightHeight);

		height = max(leftHeight, rightHeight) + 1;

		if (!isLeftBalanced || !isRightBalanced || abs(leftHeight - rightHeight) >= 2)
		{
			return false;
		}
		return true;
	}
  • 后序遍历的顺序是先处理左子树,再处理右子树,最后处理当前节点。不会重复计算子树的高度。

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

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

相关文章

python web自动化(Allure报告)

Allure详细安装请看之前的博客 1.Allure配置与⼊⻔ 运⾏⽤例&#xff0c;⽣成allure报告 pip install allure-pytest -i https://mirrors.aliyun.com/pypi/simple/ 运⾏⽤例&#xff0c;⽣成allure报告 # main.py import os import pytest if __name__ __m…

【排序算法】——归并排序(递归与非递归)含动图

制作不易&#xff0c;三连支持一下吧&#xff01;&#xff01;&#xff01; 文章目录 前言一.归并排序递归方法实现二.归并排序非递归方法实现 前言 这篇博客我们将介绍归并排序的原理和实现过程。 一、归并排序递归方法实现 基本思想&#xff1a; 归并排序&#xff08;MERGE-…

四元数学习总结(1)

导语&#xff1a;相比矩阵&#xff0c;用四元数处理3D旋转的优势是毋庸置疑的&#xff0c;但由于概念复杂&#xff0c;难于理解&#xff0c;一直令我摸不着头脑。最近学习更是发现在机器人、无人机、SLAM等先进领域&#xff0c;四元数被当成实数、整数这样的基础&#xff0c;所…

jmeter安装SSH插件

安装SSH插件 下载jar包&#xff1a; ApacheJMeter-ssh-1.2.0.jar jsch-0.1.55.jar jar包放在jmeter安装路径 lib下和lib/ext文件夹下&#xff1a; 重启jmeter即可&#xff1a;

BGP选路实验

BGP 选路实验 一、实验拓扑 二、实验要求及分析 实验要求&#xff1a; 1、使用preva1策略&#xff0c;确保R4通过R2到达192.168.10.0/24 2、使用AS_Path策略&#xff0c;确保R4通过R3到达192.168.11.0/24 3、配置MED策略&#xff0c;确保R4通过R3到达192.168.12.0/24 4、…

软考-必须要背的内容

一、设计模式 1、创建型 抽象工厂&#xff1a;提供一个接口&#xff0c;创建一系列的相关相互依赖的对象&#xff0c;无需指定具体的类&#xff1b; eg&#xff1a;系统软件&#xff0c;支持多种数据库 生成器&#xff1a;将一个复杂类的表示与构造相分离&#xff0c;使得相…

PX4使用yolo仿真环境搭建

文章目录 前言一、修改机架sdf文件二、安装yolo三、运行 前言 ubuntu20.04 PX4 1.13.3 已配置好PX4 ROS gazebo环境 一、修改机架sdf文件 将双目相机加到仿真的iris机架上 修改下图文件 添加如下&#xff1a; <include><uri>model://stereo_camera</uri>…

D - AtCoder Wallpaper(abc)

思路&#xff1a;f(c, d) f(a, b) - f(a, d) - f(c, b) 代码&#xff1a; int f(int x, int y){if(y % 2 0){y y / 2;int ans y * (x / 4) * 8;x % 4;if(x 1){ans y * 3;}else if(x 2){ans y * 6;}else if(x 3){ans y * 7;}return ans;}else{y / 2;int ans y * (x…

从零开始搭建Springboot项目脚手架4:保存操作日志

目的&#xff1a;通过AOP切面&#xff0c;统一记录接口的访问日志 1、加maven依赖 2、 增加日志类RequestLog 3、 配置AOP切面&#xff0c;把请求前的request、返回的response一起记录 package com.template.common.config;import cn.hutool.core.util.ArrayUtil; import cn.hu…

Go语言的内存泄漏如何检测和避免?

文章目录 Go语言内存泄漏的检测与避免一、内存泄漏的检测1. 使用性能分析工具2. 使用内存泄漏检测工具3. 代码审查与测试 二、内存泄漏的避免1. 使用defer关键字2. 使用垃圾回收机制3. 避免循环引用4. 使用缓冲池 Go语言内存泄漏的检测与避免 在Go语言开发中&#xff0c;内存泄…

SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?

尼恩&#xff1a;LLM大模型学习圣经PDF的起源 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;经常性的指导小伙伴们改造简历。 经过尼恩的改造之后&#xff0c;很多小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试机会&#x…

力扣刷题---961. 在长度 2N 的数组中找出重复 N 次的元素【简单】

题目描述&#x1f357; 给你一个整数数组 nums &#xff0c;该数组具有以下属性&#xff1a; nums.length 2 * n. nums 包含 n 1 个 不同的 元素 nums 中恰有一个元素重复 n 次 找出并返回重复了 n 次的那个元素。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3,3] 输…

智能猫眼锁核心解决方案以及芯片简介SSD222

书接上回&#xff0c;前篇文章我们诠释了IP 网络摄像系统的定义以及组成部分的功能&#xff0c;也大概的讲了一下所针对的市场以及举例介绍了一款相关芯片&#xff0c;详情可点击下面卡片浏览高集成IP摄像SOC处理方案简介https://blog.csdn.net/Chipsupply/article/details/139…

XILINX FPGA DDR 学习笔记(一)

DDR 内存的本质是数据的存储器&#xff0c;首先回到数据的存储上&#xff0c;数据在最底层的表现是地址。为了给每个数据进行存放并且在需要的时候读取这个数据&#xff0c;需要对数据在哪这个抽象的概念进行表述&#xff0c;我们科技树发展过程中把数据在哪用地址表示。一个数…

【C++】<知识点> 标准模板库STL(上)

文章目录 一、STL---string类 1. 常用构造函数 2. 常用操作 3. 字符串流处理 二、STL---容器 1. STL及基本概念 2. 顺序容器简介 3. 关联容器简介 4. 容器适配器简介 5. 常用成员函数 三、STL---迭代器 1. 普通迭代器 2. 双向、随机访问迭代器 3. 不同容器的迭代器…

项目思考-编辑器

1、文本生成编辑器 2、图片合成编辑器&#xff08;未完待续&#xff09; 3、文字和图像版本的技术要点&#xff0c;区别&#xff08;未完待续&#xff09; 4、编辑器的人员配置考虑&#xff0c;技术难点分析&#xff08;未完待续&#xff09; 1、文本生成编辑器

AI爆文写作:如何找对标账号的文章?告诉你一个秘密:找低粉爆款的抄!这样风险最小!

一、注册新号来训练推荐爆款的素材 首先第一点:强烈推荐注册一个专用个人微信号,通过阅读,点赞和在看等动作,训练算法为我们推荐爆款素材。 二、为什么要对标低分爆款? 2.1 什么是低粉爆款? 就是粉丝量很少,但却有很高阅读量,甚至10万+阅读的文章。 对标账号的文章…

从零开始学逆向,js逆向启蒙:有道翻译

语言&#xff1a;js、python 工具&#xff1a;pycharm、chrome浏览器F12调试、chatgpt&#xff08;补充js第三方库&#xff0c;转python&#xff09;、node.js(js运行)&#xff08;必须&#xff09; 目标&#xff1a;学习掌握基本js逆向知识。 对象&#xff1a; 有道翻译 &a…

Nginx - 安全基线配置与操作指南

文章目录 概述中间件安全基线配置手册1. 概述1.1 目的1.2 适用范围 2. Nginx基线配置2.1 版本说明2.2 安装目录2.3 用户创建2.4 二进制文件权限2.5 关闭服务器标记2.6 设置 timeout2.7 设置 NGINX 缓冲区2.8 日志配置2.9 日志切割2.10 限制访问 IP2.11 限制仅允许域名访问2.12 …

移动硬盘难题:不显示容量与无法访问的解决策略

在使用移动硬盘的过程中&#xff0c;有时会遇到一些棘手的问题&#xff0c;比如移动硬盘不显示容量且无法访问。这种情况让人十分头疼&#xff0c;因为它不仅影响了数据的正常使用&#xff0c;还可能导致重要数据的丢失。接下来&#xff0c;我们就来详细探讨一下这个问题及其解…