【高阶数据结构】AVL树的旋转与底层(C++实现)

news2024/9/17 7:09:05

1.AVL树的概念及作用

2.AVL树插入数据的规则

1.按照搜索树的规则插入,然后更新父亲的平衡因子

2.更新父亲的平衡因子后,如果出现一下三种情况需要进行相应处理

3.AVL树的旋转

3.1右单旋

右单旋的所有情况可以抽象为上图:图中,a,b,c均为高度为h(h取0,1,2......)的AVL子树。

插入新结点后,开始旋转:parent->left=subLR; subL->right=parent;

旋转过后依然满足搜索树的规则。

但旋转并没有那么简单,还要调整父子关系,调整根结点,更新平衡因子!

代码:

//右单旋
void RotateR(Node* parent) {
	//旋转,改变树形结构
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	parent->_left = subLR;
	subL->_right = parent;
	//调整父子关系
	Node* ppNode = parent->_parent;
	if (subLR) subLR->_parent = parent;
	parent->_parent = subL;
	//处理根节点
	if (parent==_root) {
		_root = subL;
		subL->_parent = nullptr;
	}
	else {
		subL->_parent == ppNode;
		if (ppNode->_left == parent) ppNode->_left = subL;
		else ppNode->_right = subL;
	}
	//更新平衡因子
	subL->_bf = parent->_bf = 0;
}

3.2左单旋

思路和代码与右单旋类似。

void RotateL(Node* parent) {
	//旋转,改变树形结构
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	parent->_right = subRL;
	subR->_left = parent;
	//调整父子关系
	Node* ppNode = parent->_parent;
	if (subRL) subRL->_parent = parent;
	parent->_parent = subR;
	//处理根节点
	if (parent == _root) {
		_root = subR;
		subR->_parent = nullptr;
	}
	else {
		subR->_parent = ppNode;
		if (ppNode->_left == parent) ppNode->_left = subR;
		else ppNode->_right = subR;
	}
	//更新平衡因子
	subR->_bf = parent->_bf = 0;
}

3.3左右双旋

对比一下右单旋的图片,左右双旋的抽象图是把30结点的右子树细致的分为60的结点和60的左右子树。

双旋的情况就是在60这个子树上插入一个新的结点,插入过后需要经过两次旋转。旋转后的结果就是30和90都变成了60的子树。原本为60的子树,变成了30和90的子树。

代码:

//左右双旋
void RotateLR(Node* parent) {
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	int bf = subLR->_bf;
	RotateL(parent->_left);
	RotateR(parent);
	subLR->_bf = 0;
	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);//理论上不存在这种情况
}

3.4右左双旋

右左双旋与左右双旋正好相反。

代码:

//右左双旋
void RotateRL(Node* parent) {
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	int bf = subRL->_bf;
	RotateR(parent->_right);
	RotateL(parent);
	subRL->_bf = 0;
	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);//理论上这种情况不存在
}

4.AVL树的底层

模拟实现:

其中:AVLTree_Test1()和AVLTree_Test2()为测试代码。

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


//KV模型
namespace key_value {
	template<class k, class v>
	//class BinarySearchTreeNode
	struct AVLTreeNode {
		AVLTreeNode(const pair<k,v>& kv)
			:_kv(kv)
			{}
		AVLTreeNode<k, v>* _left = nullptr;
		AVLTreeNode<k, v>* _right = nullptr;
		AVLTreeNode<k, v>* _parent = nullptr;
		pair<k, v> _kv;
		int _bf = 0;//balance factor
	};


	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* parent = nullptr;
			Node* cur = _root;
			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 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);
					break;
				}
				else assert(false);
			}
			return true;
		}
		void Inorder() {
			_Inorder(_root);
		}
		Node* Find(const k& key) {
			Node* cur = _root;
			while (cur) {
				if (cur->_kv.first < key) {
					cur = cur->_right;
				}
				else if (cur->_kv.first > key) {
					cur = cur->_left;
				}
				else return cur;
			}
			return nullptr;
		}
		//右单旋
		void RotateR(Node* parent) {
			//旋转,改变树形结构
			Node* subL = parent->_left;
			Node* subLR = subL->_right;
			parent->_left = subLR;
			subL->_right = parent;
			//调整父子关系
			Node* ppNode = parent->_parent;
			if (subLR) subLR->_parent = parent;
			parent->_parent = subL;
			//处理根节点
			if (parent==_root) {
				_root = subL;
				subL->_parent = nullptr;
			}
			else {
				subL->_parent = ppNode;
				if (ppNode->_left == parent) ppNode->_left = subL;
				else ppNode->_right = subL;
			}
			//更新平衡因子
			subL->_bf = parent->_bf = 0;
		}
		void RotateL(Node* parent) {
			//旋转,改变树形结构
			Node* subR = parent->_right;
			Node* subRL = subR->_left;
			parent->_right = subRL;
			subR->_left = parent;
			//调整父子关系
			Node* ppNode = parent->_parent;
			if (subRL) subRL->_parent = parent;
			parent->_parent = subR;
			//处理根节点
			if (parent == _root) {
				_root = subR;
				subR->_parent = nullptr;
			}
			else {
				subR->_parent = ppNode;
				if (ppNode->_left == parent) ppNode->_left = subR;
				else ppNode->_right = subR;
			}
			//更新平衡因子
			subR->_bf = parent->_bf = 0;
		}
		//右左双旋
		void RotateRL(Node* parent) {
			Node* subR = parent->_right;
			Node* subRL = subR->_left;
			int bf = subRL->_bf;
			RotateR(parent->_right);
			RotateL(parent);
			subRL->_bf = 0;
			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) {
			Node* subL = parent->_left;
			Node* subLR = subL->_right;
			int bf = subLR->_bf;
			RotateL(parent->_left);
			RotateR(parent);
			subLR->_bf = 0;
			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);//理论上不存在这种情况
		}
		bool IsBalance() {
			return _IsBalance(_root);
		}
		int Height() {
			return _Height(_root);
		}
		int Size() {
			return _size(_root);
		}
	private:
		int _size(Node* root) {
			if (root == nullptr) return 0;
			return _size(root->_left) + _size(root->_right) + 1;
		}
		int _Height(Node* root) {
			if (root == nullptr) return 0;
			else
				return max(_Height(root->_left), _Height(root->_right)) + 1;
		}
		bool _IsBalance(Node* root) {
			if (root == nullptr) return true;
			else {
				int leftHeight = _Height(root->_left);
				int rightHeight = _Height(root->_right);
				if (abs(leftHeight - rightHeight) >= 2) {
					cout << root->_kv.first << endl;
					return false;
				}
				//顺便检查一下平衡因子是否有问题
				if ((rightHeight - leftHeight) != root->_bf) {
					cout << root->_kv.first << endl;
					return false;
				}
				return _IsBalance(root->_left) && _IsBalance(root->_right);
			}
		}
		void _Inorder(Node* root) {
			if (root == nullptr) return;
			_Inorder(root->_left);
			cout << root->_kv.first << ":" << root->_kv.second << endl;
			_Inorder(root->_right);
		}
	private:
		Node* _root = nullptr;
	};
}

void AVLTree_Test1() {
	//int arr[] = { 8,3,1,10,6,4,7,14,13 };
	int arr[] = { 4,2,6,1,3,5,15,7,16,14 };
	key_value::AVLTree<int, int> t1;
	for (auto& e : arr) {
		/*if (e == 4) {
			int i = 0;
		}*/
		t1.insert({e,e});
	}
	t1.Inorder();
	cout << t1.IsBalance() << endl;
}

void AVLTree_Test2() {
	const int N = 1000000;
	vector<int> v;
	srand(time(0));
	for (int i = 0; i < N; i++) {
		v.push_back(rand()+i);
	}
	size_t begin1 = clock();
	key_value::AVLTree<int, int> t;
	for (auto e : v) {
		t.insert(make_pair(e, e));
	}
	size_t end1 = clock();

	size_t begin2 = clock();
	for (auto e : v) {
		t.Find(e);
	}
	size_t end2 = clock();
	cout <<"insert"<<"->"<< end1 - begin1 << endl;
	cout << "Balance->"<<t.IsBalance() << endl;
	cout << "Height->" << t.Height() << endl;
	cout << "Size->" << t.Size() << endl;
	cout << "Find->" << end2 - begin2 << endl;
}

测试:

#include"AVLTree.h"
int main() {
	//AVLTree_Test1();
	AVLTree_Test2();
	return 0;
}

从程序运行结果可知:在Release版本下,插入100万个随机数,其中实际插入约63万个数值。

插入耗时:173毫秒(主要是new结点花时间)。

AVL树高度:22。

把63万个随机数全部查找一遍耗时:不到1毫秒。

可见AVL树的查找效率是非常高的!

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

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

相关文章

【识人】感情与交友中,如何判断一个人的性格,以及是否值得交往和相处

【识人】感情与交友中&#xff0c;如何判断一个人的性格&#xff0c;以及是否值得交往和相处 文章目录 序言正文1、学会筛选&#xff0c;贴标签&#xff0c;学会区别对待&#xff0c;2、男生女生一定要在年轻的时候学会对外在祛魅3、培养付出意识&#xff0c;学会顶风相见。4、…

protobuf —— 认识和安装

protobuf —— 认识和安装 什么是序列化和反序列化有哪些常见的什么是序列化和反序列化工具Protobuf安装安装依赖开始安装 连接动态库一些遗留问题 我们今天来看一个序列化和反序列化的工具&#xff1a;protobuf。 什么是序列化和反序列化 序列化&#xff08;Serialization&a…

Yolov9调用COCOAPI生成APs,APm,APl

最近在做小目标检测的东西&#xff0c;因为后期毕业论文需要&#xff0c;所以开始使用Yolov9模型&#xff0c;运行val.py的时候不会自己产生小目标的AP指标&#xff0c;所以研究了一下&#xff0c;步骤非常简单&#xff1a; 第一步&#xff1a; 在数据集中生成json格式的Annota…

【LLM多模态】综述Visual Instruction Tuning towards General-Purpose Multimodal Model

note 文章目录 note论文1. 论文试图解决什么问题2. 这是否是一个新的问题3. 这篇文章要验证一个什么科学假设4. 有哪些相关研究&#xff1f;如何归类&#xff1f;谁是这一课题在领域内值得关注的研究员&#xff1f;5. 论文中提到的解决方案之关键是什么&#xff1f;6. 论文中的…

AIGC时代算法工程师的面试秘籍(2024.4.29-5.12第十三式) |【三年面试五年模拟】

写在前面 【三年面试五年模拟】旨在整理&挖掘AI算法工程师在实习/校招/社招时所需的干货知识点与面试方法&#xff0c;力求让读者在获得心仪offer的同时&#xff0c;增强技术基本面。也欢迎大家提出宝贵的优化建议&#xff0c;一起交流学习&#x1f4aa; 欢迎大家关注Rocky…

LVS精益价值管理系统 DownLoad.aspx 任意文件读取漏洞复现

0x01 产品简介 LVS精益价值管理系统是杭州吉拉科技有限公司研发的一款专注于企业精益化管理和价值流优化的解决方案。该系统通过集成先进的数据分析工具、可视化的价值流映射技术和灵活的流程改善机制&#xff0c;帮助企业实现高效、低耗、高质量的生产和服务。 0x02 漏洞概述…

【数据库】基于PyMySQL连接并使用数据库(代码示例)

这里写目录标题 前言1、安装PyMySQL2、打开要连接的数据库3、创建数据库连接4、获取数据库版本5、新建数据库表6、向表中插入数据7、查询表中的相关记录8、更新表中的相关记录9、删除表中的相关记录10、关闭游标和连接完整代码 前言 本文演示了如何基于PyMySQL使用代码来创建数…

异步获取线程执行结果,JDK中的Future、Netty中的Future和Promise对比

JDK中的Future和Netty中的Future、Promise的关系 三者源头追溯 Netty中的Future与JDK中的Future同名&#xff0c;但是是两个不同的接口。Netty中的Future继承自JDK的Future&#xff0c;而Promise又对Netty中的Future进行了扩展。 JDK中的Future源自JUC并发包&#xff1a; Net…

打卡信奥刷题(19)用Scratch图形化工具信奥B3972 [语言月赛 202405] 二进制 题解

进制转换是经典的编程题&#xff0c;尤其是10进制转换为2进制。方法是拿给定的数&#xff0c;不断地除2&#xff0c;将余数放在对应的位置&#xff0c;剩下的数为对应数除2向下取整 [语言月赛 202405] 二进制 题目描述 在介绍十进制转二进制的篇目中&#xff0c;我们总会看到…

一文读懂:http免费升级https

背景&#xff1a; 随着现在全民网络安全意识的日益提升&#xff0c;各个网站需要实现的https数量也随之提升&#xff0c;那么如何将原本网站的http访问方式升级为https呢&#xff1f; 该内容为如何免费将网站的http访问升级为https访问 论https的加密逻辑&#xff1a; 步骤 …

【计算机网络】初识Tcp协议

&#x1f4bb;文章目录 &#x1f4c4;前言Tcp基础概念Tcp 的报文格式三次握手四次挥手 Tcp的滑动窗口机制概念超时重传机制高速重传 TCP传输控制机制流量控制拥堵控制慢启动 Tcp的性能优化机制延迟应答捎带应答 &#x1f4d3;总结 &#x1f4c4;前言 TCP三次握手、四次挥手&…

element-ui 实现输入框下拉树组件(2024-05-23)

用element-ui的 el-input&#xff0c;el-tree&#xff0c;el-popover组件组合封装 import url("//unpkg.com/element-ui2.15.14/lib/theme-chalk/index.css"); <script src"//unpkg.com/vue2/dist/vue.js"></script> <script src"//…

SEO之核心关键词(一)

初创企业需要建站的朋友看这篇文章&#xff0c;谢谢支持&#xff1a; 我给不会敲代码又想搭建网站的人建议新手上云 选择关键词的第一步是确定网站核心关键词。 核心关键词通常就是网站首页的目标关键词。一般来说&#xff0c;整个网站会有很多目标关键词&#xff0c;这些关键…

记一次MySQL执行修改语句超时问题

异常问题 原因分析 这个问题发生在开发环境&#xff0c;怀疑是提交事务时终止项目运行&#xff0c;没有提交该事务&#xff0c;造成死锁 调试该事务时时间太长&#xff0c;为什么说有这个原因呢&#xff0c;因为通过查找日志显示 The client was disconnected by the server …

【二叉树算法题记录】236. 二叉树的最近公共祖先

题目链接 题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个…

Day04-Maven,SpringBoot,Tomcat下载及相关配置

1. maven 2. MaVen是一款管理和构建java项目的工具 2.1 概述 mvn -v2.1 配置Maven环境&#xff08;当前工程&#xff09; 全局配置maven 3. SpringBoot 3.1 SpringBoot入门 package com.jingwei.controller;import org.springframework.web.bind.annotation.…

一个和蔼可亲的Python库,用Gooey为你的程序添加GUI

大家好&#xff0c;你有没有遇到过这样的情况&#xff1a;你写了一个非常棒的命令行程序&#xff0c;但当你分享给朋友或同事时&#xff0c;他们却因为害怕命令行而不愿意使用&#xff1f;这时候&#xff0c;一个简洁美观的图形用户界面&#xff08;GUI&#xff09;就派上用场了…

产品经理-需求分析(三)

1. 需求分析 从业务的需要出发&#xff0c;确定业务目的和目标&#xff0c;将业务需求转为产品需求 1.1 业务需求 业务需求 业务动机 业务目标 就是最根本的动机和目标成果&#xff0c;通过这个需求解决特定的问题 1.2 产品需求 产品需求 解决方案 产品结构 产品流程…

CI/CD 管道中的自动化测试:类型和阶段

在上一篇文章中&#xff0c;我们讨论了敏捷团队自动化测试用例的各种用例。其中一种情况是&#xff0c;团队希望将测试与每个构建集成&#xff0c;并将持续集成作为构建过程的一部分。 在本文中&#xff0c;我们将讨论持续集成/持续交付平台中的集成测试。 让我们先从基础知识…

Spring Cloud | 服务 “注册与发现“ 框架 : Eureka框架

目录&#xff1a; Eureka 的 "工作机制" :一、Eureka 的 "工作原理" ( 两大组件 ) :1.1 Eureka Server ( 服务注册中心 )1.2 Eureka Client ( 服务/服务实例&#xff0c;其存在 "两种角色" : ①服务提供者 ②服务消费者 ) :Eureka Client 的 含义…