c++简单实现avl树

文章目录

  • AVL树
    • 节点类
      • 节点类的构造函数
    • AVL
      • insert()插入
        • RotateL(左单旋)
        • RotateR(右单旋)
        • RotateLR(右双旋)
        • RotateRL(左双旋)
      • Find(查找)
      • IsBalance(检查是否是avl树)

AVL树

AVL树:又名高度平衡树,在二叉搜索树的基础上加上了一个条件,条件是左右子树高度差不超过1
在这里插入图片描述

节点类

节点类:更好的管理节点
这里我选择的是右子树-左子树作为平衡因子,有平衡因子更方便实现

template<class K,class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	int _bf;//balance factor 平衡因子:右子树-左子树的值 不超过1
	pair<K, V> _kv;
};

节点类的构造函数

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

	}

AVL

因为是高度平衡所以我们要一直控制他的高度,使其达到平衡

template<class K, class V>
class AVLTree
{
public:
	typedef AVLTreeNode<K, V> Node;
private:
	Node* _root;
};

insert()插入

插入规则:
1、按照搜索树的规则
2、更新平衡因子
3、更新因子规则 : c是p的左边 bf-- c是p的右边 ++
4、更新因子后, p的bf == 0 那么不会影响爷爷,说明更新前 p的bf为1 or -1 ,p的矮的那一边插入了节点.
如果更新后 p的bf == 1 or -1 那么就会影响爷爷.并且往上更新
更新后p的bf==2 那么旋转处理

	bool Insert(const pair<K,V>& kv)
	{
		if (_root == nullptr)//树为空的情况
		{
			_root = new Node(kv);//直接做根节点
			return true;
		}
		Node* parent = nullptr;//因为cur是局部变量函数走完会销毁,所以增加一个指针 链接cur
		Node* cur = _root;//赋值为根代替根操作
		while (cur)
		{
			//插入的值的key值比较
			//大往右 小往左
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else//如果有这个值了返回false 因为avl树中不能有同样的值
			{
				return false;
			}
		}
		cur = new Node(kv);//new一个新节点赋值给cur
		if (parent->_kv.first < kv.first)//大就链右边 小链左边
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;//让新节点指向其父节点
		//因为插入了
		//所以要修改parent的平衡因子
		while (parent)
		{
			if (cur == parent->_left)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}
			if (parent->_bf == 0)
				//更新 前为 1或者-1 更新后树就平衡了
				//结束
			{
				break;
			}
			else if (parent->_bf == 1 || parent->_bf - 1)
				//之前为 0 更新会影响祖先
				//继续往上
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
				//需要发生旋转
				//旋转是为了满足搜索树的规则
				//减少当前树的高度到达平衡
				//旋转后结束
			{
				if (parent->_bf == 2 && cur->_bf == 1)//左单旋
				{
					RotateL(parent);
				}
				else if(parent->_bf == -2 && cur->_bf == -1)//右单旋
				{
					RotateR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == -1)//左双旋
				{
					RotateRL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)//右双旋
				{
					RotateLR(parent);
				}
				break;
			}
			else
			{
				assert(false);
			}
		}
	return true;
}
RotateL(左单旋)

以图来介绍
25是传过来的parent
我们定义parent的右为subR(也就是cur),定义cur的左为subRL(也就是cur->left)
我们旋转的时候把subRL给到parent的左边
然后把parent给到subR的左边
这样子就完成了左单旋
最后修改一下平衡因子就可以了

在这里插入图片描述

void RotateL(Node* parent)//左旋转
{
	//定义p的右边为 subR p的右节点的左节点为 subRL
	//subRL可能是一个子树的根节点
	Node* subR = parent->_right;//cur
	Node* subRL = subR->_left;//cur->_left
	//把p的右边给  p的右边的左边 因为他一点比p大比p的右边小
	parent->_right = subRL;
	//把p的右边 链接p 让p的右边成为根
	if(subRL)//可能为空
		subRL->_parent = parent;
	subR->_left = parent;
	//保存一份p的父节点 因为原来的p要改父节点了
	Node* ppnode = parent->_parent;
	//p的父节点指向他原来的右子树
	parent->_parent = subR;
	//判断p是不是树根节点
	//如果是的话 让root改一下 他的父节点设置为空
	if (parent == _root)
	{
		_root = subR;
		subR->_parent = nullptr;
	}
	else//如果不是树根节点
	{
		//如果原来的p是p父节点的左边 那么让他的左边链接现在的sub(原p右节点)
		if (ppnode->_left == parent)
		{
			ppnode->_left = subR;
		}
		//如果是右边 那么同理
		else
		{
			ppnode->_right = subR;
		}
		subR->_parent = ppnode;
	}
	//最后改好后让他的平衡因子变一下 
	//因为已经平衡了所以p 的因子成为了0 subr 左右树高平衡了所以也要改成0
	parent->_bf = 0;
	subR->_bf = 0;
}
RotateR(右单旋)

以图来介绍
20是传过来的parent
我们定义parent的左为subL(也就是cur),定义cur的右为subLR(也就是cur->left)
我们旋转的时候把subRL给到parent的左边
然后把parent给到subL的右边
这样子就完成了右单旋
最后修改一下平衡因子就可以了

在这里插入图片描述

void RotateR(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;
}
RotateLR(右双旋)

以图解释
右双旋就是先左单旋,后右单旋
对subL进行左单旋,获得图2
然后parent进行右单旋
获得图3
最后后平衡一下平平衡因子

在这里插入图片描述

void RotateLR(Node* parent)//先左单旋 后右单旋 右双旋
{
	Node* subL = parent->_left;
	Node* subLR = parent->_left->_right;
	RotateL(parent->_left);
	RotateR(parent);
	//用 _bf来查看是谁插入
	if (subLR->_bf == -1)
	{
		//b位置插入
		subLR->_bf = 0;
		subL->_bf = 1;
		parent->_bf = 0;
	}
	else if (subLR->_bf == 1)
	{
		//c位置插入
		subLR->_bf = 0;
		subL->_bf = -1;
		parent->_bf = 0;

	}
	else if (subLR->_bf == 0)
	{
		//新增
		subLR->_bf = 0;
		subL->_bf = 0;
		parent->_bf = 0;
	}
	else
	{
		assert(false);
	}
}
RotateRL(左双旋)

以图解释
左双旋就是先右单旋,后左单旋
对subR进行右单旋,获得图2
然后对parent进行左单旋
获得图3
最后后平衡一下平平衡因子

在这里插入图片描述

Find(查找)

查找一个数
大了往左找,小了往右找

bool _Find(Node* _root,const K& x)
{
	if (_root == nullptr)
	{
		return false;
	}
	if (_root->_kv.first == x)
	{
		return true;
	}
	return _Find(_root->_left,x) || _Find(_root->_right,x);
}
bool Find(const K& x)
{
	return _Find(_root, x);
}

IsBalance(检查是否是avl树)

检查是否是AVL树
从规则入手

int _Height(Node* root)
{
	if (root == nullptr)
		return 0;
	return _Height(root->_left) + _Height(root->_right) + 1;
}
bool _IsBalance(Node* Root)
{
	// 空树也是AVL树
	if (nullptr == Root) return true;

	// 计算Root节点的平衡因子:即Root左右子树的高度差
	int leftHeight = _Height(Root->_left);
	int rightHeight = _Height(Root->_right);
	int diff = rightHeight - leftHeight;
	// 如果计算出的平衡因子与Root的平衡因子不相等,或者
	// Root平衡因子的绝对值超过1,则一定不是AVL树
	if (diff != Root->_bf || (diff > 1 || diff < -1))
		return false;
	// Root的左和右如果都是AVL树,则该树一定是AVL树
	return _IsBalance(Root->_left) && _IsBalance(Root -> _right);
}
bool IsBalance()
{
	_IsBalance(_root);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【并查集】模版

【模板】并查集 - 洛谷 #include <bits/stdc.h> using namespace std; const int N2e59; int a[N]; int Find(int x) {if(xa[x]){return x;}else{a[x]Find(a[x]);return a[x];} } void push(int x,int y) {a[Find(x)]Find(y);return ; } int main() {int n,m; cin>>…

(十七)【Jmeter】取样器(Sampler)之JSR223取样器

该实例使用python 2.7.3的插件,jython-standalone-2.7.3.jar:https://www.123pan.com/s/VppKjv-5YvTv.html 提取码:tfsX 把该插件放在Jmeter安装的:apache-jmeter-5.6.3\lib\ext目录下: 简述 JSR是Java Specification Requests的缩写,意思是Java规范提案 操作路径如下: …

Linux-新手小白速秒Hadoop集群全生态搭建(图文混编超详细)

在之前的文章中&#xff0c;我教会大家如何一步一步搭建一个Hadoop集群&#xff0c;但是只提供了代码&#xff0c;怕有些朋友会在一些地方产生疑惑&#xff0c;今天我来以图文混排的方式&#xff0c;一站式交给大家如何搭建一个Hadoop高可用集群包括&#xff08;HadoopHA&#…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:SideBarContainer)

提供侧边栏可以显示和隐藏的侧边栏容器&#xff0c;通过子组件定义侧边栏和内容区&#xff0c;第一个子组件表示侧边栏&#xff0c;第二个子组件表示内容区。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起…

第九届多媒体系统和信号处理国际会议(ICMSSP 2024)即将召开!

2024年第九届多媒体系统和信号处理国际会议&#xff08;ICMSSP 2024&#xff09;将在5月24-26日在泰国曼谷举行。ICMSSP 2024旨在展示多媒体系统和信号处理等相关主题的最新研究和成果&#xff0c;为不同领域的专家代表提供了面对面交流新想法以及应用经验的机会&#xff0c;建…

【经验总结】ubuntu 20.04 git 上传本地文件给 github,并解决出现的问题

1. 在GitHub 上创建仓库 登录 GitHub 个人网站 点击 New 填写 Repository name, 以及 Description (optional) 选择 Public &#xff0c; 并添加 Add a README file 点击 Create repository github repository 创建成功 2. 设置SSH key 在本地 bash 运行&#xff1a;…

sqllab第十八关通关笔记

知识点&#xff1a; UA注入 不进行url解析&#xff0c;不能使用 %20 编码等操作出现在User-agent字段中一般为insert语句 insert 表名(字段1&#xff0c;字段2&#xff0c;。。。) values(数据1&#xff0c;数据2&#xff0c;。。。) 通过admin admin进行登录发现页面打印出了…

arp动态表缓存清除

一、arp表里清除表状态&#xff1a; 1&#xff0c;Delay&#xff1a;请求arp 2&#xff0c;Reachab&#xff1a;响应arp 3&#xff0c;Stale此状态下&#xff0c;待gc_stale_time超时后&#xff0c;准备gc_interval定期清理 二、限制条件 base_reachable_time&#xff1a;后变…

数据结构的概念大合集04(队列)

概念大合集04 1、队列1.1 队列的定义1.2队列的顺序存储1.2.1 顺序队1.2.2 顺序队的基本运算的基本思想1.2.3 顺序队的4要素的基本思想 1.3 环形队列1.3.1 环形队列的定义1.3.1 环形队列的实现 1.4 队列的链式存储1.4.1 链队1.4.2 链队的实现方式1.4.3 链队的4要素的基本思想 1.…

双指针 | 移动零 | 复写零

1.移动零 题目描述&#xff1a; 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 示例&#xff1a; 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0]解题思路&#xff1a; right指针一直往后移动&#xff0c;当…

Java实现简单的通讯录

每日一言 泪眼问花花不语&#xff0c;乱红飞过秋千去。 —欧阳修- 简单的通讯录实现&#xff0c;跟写Java实现图书管理系统差不多&#xff0c;用到的知识也差不多&#xff0c;就当个小练习&#xff0c;练习一下写Java程序的手感。 Java实现图书管理系统 关于通讯录的代码都写…

【JVM】(内存区域划分 为什么要划分 具体如何分 类加载机制 类加载基本流程 双亲委派模型 类加载器 垃圾回收机制(GC))

文章目录 内存区域划分为什么要划分具体如何分 类加载机制类加载基本流程双亲委派模型类加载器 垃圾回收机制&#xff08;GC&#xff09; 内存区域划分 为什么要划分 JVM启动的时候会申请到一整个很大的内存区域,JVM是一个应用程序,要从操作系统这里申请内存,JVM就需要根据,把…

Android Studio字体大小调节

外观页面字体调节 settings->Appearance->User cunstom font 代码字体调节 Settings->Editor->Font此时logcat窗口、Build窗口和Ternimal窗口字体大小也会同步调节&#xff08;2023.2.1版本上验证&#xff09;

基于Springboot和Redis实现的快递代取系统

1.项目简介 本项目基于springboot框架开发而成&#xff0c;前端采用bootstrap和layer框架开发&#xff0c;系统功能完整&#xff0c;界面简洁大方&#xff0c;比较适合做毕业设计使用。 本项目主要实现了代取快递的信息管理功能&#xff0c;使用角色有三类&#xff1a;一是客…

Elasticsearch 主副分片切换过程中对业务写入有影响吗

&#x1f34a;&#x1f349;&#x1f34b; 先说下结论&#xff0c;只要集群中的工作节点过半&#xff0c;有候选的master节点&#xff0c;挂掉的节点中不同时包含索引的主分片和副分片&#xff0c;那么ES是可以做到让业务无感知的进行主副分片切换的。 蓝胖子会先讲解下ES集群写…

ARM_基础之RAS

Reliability, Availability, and Serviceability (RAS), for A-profile architecture 源自 https://developer.arm.com/documentation/102105/latest/ 1 Introduction to RAS 1.1 Faults,Errors,and failures 三个概念的区分&#xff1a; • A failure is the event of devia…

外包干了3天,技术明显进步。。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;19年通过校招进入南京某软件公司&#xff0c;干了接近2年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了2年的功能测试&…

16、技巧之九: 修改参数,如何让表格翻页滚动到底部?【Selenium+Python3网页自动化总结】

1、问题提出 在网页配置参数时&#xff0c;输入参数名称搜索&#xff0c;搜出来的同名参数结果有多个&#xff0c;分布在一个表格的不同行&#xff0c;表格是动态加载的&#xff0c;需要滚动鼠标才能把所出参数找出来。用selenium怎么实现这种参数修改&#xff1f; 2、网页元素…

JVM学习-JVM的自动优化

目录 1.语法糖 1.1默认构造器 1.2自动拆装箱 1.3泛型集合取值 1.4可变参数实现 1.5 foreach循环 1.6 switch配合String使用 1.7 switch配合枚举使用​编辑 1.8 try-with-resources 1.9方法重写的桥接方法 2.运行时优化 2.1分层优化以及逃逸分析 2.2方法内联 2.3字段优化 JVM会…

产品推荐 - 基于FPGA XC7K325T+DSP TMS320C6678的双目交汇视觉图像处理平台

一、产品概述 TES601是一款基于FPGA与DSP协同处理架构的双目交汇视觉图像处理系统平台&#xff0c;该平台采用1片TI的KeyStone系列多核浮点/定点DSP TMS320C6678作为核心处理单元&#xff0c;来完成视觉图像处理算法&#xff0c;采用1片Xilinx的Kintex-7系列FPGA XC7K325T作为视…