平衡二叉树

AVL简称平衡二叉树,缩写为BBST,由苏联数学家 Adelse-Velskil 和 Landis 在 1962 年提出。

二叉树是动态查找的典范,但在极限情况下,二叉树的查找效果等同于链表,而平衡二叉树可以完美的达到 log ⁡ 2 n \log_2 n log2n

一直想深入的研究一下,并手写平衡二叉树的插入、删除代码。

可惜的是,国内数据结构的神级教材:阎蔚敏老师主编《数据结构》一书中并未看到关于AVL树的代码。

例子:

将17,9,2,12,14,26,33,15,40,23,25一次插入到一棵初始化为空的AVL树中,画出该二叉平衡树。

解:过程和结果如下图所示。

在这里插入图片描述

所谓平衡二叉树,就是指二叉树的左、右子树的深度差不超过2。每当插入一个新的元素,导致左右子树的深度超过2层时,需要对二叉树的失衡节点进行平衡,保持左右子树高度差在-1到1之间。

可以使用两个整数来表示左右子树的深度,前面一个表示左子树的层数,右边一个代表右子树的层数。

调整时,首先需要找到要平衡的节点。找到调整节点后,处理的方法有4种:

上图中圆标号1的是左-左结构,标号2的是左-右结构,标号3的是右-右,标号4的是右-左结构,这4种结构的处理方式各有不同。

  1. 左-左结构,即(2,1)结构

中间节点当作父节点,最上面的节点当作右节点,最下边节点当作左节点

  1. 左-右结构,即(2,-1)结构

最下面节点当作父节点,父节点当作右节点,中间节点当作左节点

  1. 右-右结构,即(-2,-1)结构

中间节点当作父节点,最上面的节点当作左节点,最下边节点当作右节点

  1. 右-左结构,即(-2,1)结构

最下面节点当作父节点,最上面节点当作左节点,中间节点当作右节点

编程中,计算左、右子树深度的代码如下:


int deep(BBST* b) {
	if (b == 0)
	{
		return 0;
	}

	int ld =  deep(b->lchild);
		
	int rd = deep(b->rchild) ;
	
	return ld > rd ? ld + 1 : rd + 1;
}

有了上面的理论和编程基础,我们可以慢慢的调试并手动写出平衡二叉树的插入代码:



int BBSTree::insert(ELEMENT* e) {
	if (mTree == 0)
	{
		mTree = newnode(e);
		mSize = 1;
		return 1;
	}

	BBST* t = mTree;

	BBST* tc = 0;
	Stack s;

	ELEMENT elem;

	while (1) {
		if (e->e == t->data.e) {
			return 0;
		}
		else if (e->e > t->data.e)
		{
			if (t->rchild == 0)
			{
				tc = newnode(e);
				tc->parent = t;
				t->rchild = tc;
				mSize++;
				break;
			}
			else {			
				elem.e = (unsigned long long)t;
				s.push((ELEMENT*)&elem);

				t = t->rchild;				
			}
		}
		else {
			if (t->lchild == 0)
			{
				tc = newnode(e);
				tc->parent = t;

				t->lchild = tc;
				mSize++;
				break;
			}
			else {
				elem.e = (unsigned long long)t;
				s.push((ELEMENT*)&elem);

				t = t->lchild;		
			}
		}
	}

	while (s.isEmpty() == 0) {

		s.pop(&elem);
		BBST* b = (BBST*)elem.e;
		b->ld = deep(b->lchild);
		b->rd = deep(b->rchild);

		t->ld = deep(t->lchild);
		t->rd = deep(t->rchild);

		int high_diff = b->ld - b->rd;
		int low_diff = t->ld - t->rd;
		if(high_diff == 2 && low_diff == 1)
		{
			BBST* f = (BBST*)b->parent;
			if (f&&f->lchild == b)
			{
				f->lchild = t;
			}
			else if (f&&f->rchild == b)
			{
				f->rchild = t;
			}
			t->parent = f;

			BBST* tr = t->rchild;
			t->rchild = b;
			b->parent = t;
		
			b->lchild = tr;
			if (tr)
			{
				tr->parent = b;
			}

			if (b == mTree)
			{
				mTree = t;
			}
		}
		else if (high_diff == 2 && low_diff == -1)
		{
			BBST* f = (BBST*)b->parent;
			if (f->lchild == b)
			{
				f->lchild = tc;
			}
			else if (f->rchild == b)
			{
				f->rchild = tc;
			}
			tc->parent = f;

			t->parent = tc;
			if (tc->lchild)
			{
				tc->lchild->parent = t;
			}
			
			t->rchild = tc->lchild;

			b->parent = tc;
			if (tc->rchild)
			{
				tc->rchild->parent = b;
			}
			
			b->lchild = tc->rchild;

			tc->rchild = b;
			tc->lchild = t;		

			if (b == mTree)
			{
				mTree = tc;
			}
		}
		else if (high_diff == -2 && low_diff == 1)
		{
			BBST* f = (BBST*)b->parent;
			if (f&&f->lchild == b)
			{
				f->lchild = tc;
			}
			else if (f&&f->rchild == b)
			{
				f->rchild = tc;
			}
			tc->parent = f;

			b->parent = tc;
			b->rchild = tc->lchild;
			if (tc->lchild)
			{
				tc->lchild->parent = b;
			}

			t->parent = tc;
			t->lchild = tc->rchild;
			if (tc->rchild)
			{
				tc->rchild->parent = t;
			}
			
			tc->rchild = t;
			tc->lchild = b;

			if (b == mTree)
			{
				mTree = tc;
			}
		}
		else if (high_diff == -2 && low_diff == -1)
		{
			BBST* f = (BBST*)b->parent;
			if (f && f->lchild == b)
			{
				f->lchild = t;
			}
			else if (f && f->rchild == b)
			{
				f->rchild = t;
			}
			t->parent = f;

			BBST* tl = t->lchild;
			t->lchild = b;
			b->parent = t;
		
			b->rchild = tl;
			if (tl)
			{
				tl->parent = b;
			}

			if (b == mTree)
			{
				mTree = t;
			}
		}
		tc = t;
		t = b;
	}

	return 0;
}


完整代码地址:
https://github.com/satadriver/dataStruct

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

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

相关文章

JVM 内存分析工具 Memory Analyzer Tool(MAT)的深度讲解

目录 一. 前言 二. MAT 使用场景及主要解决问题 三. MAT 基础概念 3.1. Heap Dump 3.2. Shallow Heap 3.3. Retained Set 3.4. Retained Heap 3.5. Dominator Tree 3.6. OQL 3.7. references 四. MAT 功能概述 4.1. 内存分布 4.2. 对象间依赖 4.3. 对象状态 4.4…

docker容器配置MySQL与远程连接设置(纯步骤)

以下为ubuntu20.04环境,默认已安装docker,没安装的网上随便找个教程就好了 拉去mysql镜像 docker pull mysql这样是默认拉取最新的版本latest 这样是指定版本拉取 docker pull mysql:5.7查看已安装的mysql镜像 docker images通过镜像生成容器 docke…

Git的安装以及SSH配置

前言 近期工作需要,所以版本管理工具要用到Git,某些操作需要ssh进行操作,在某次操作中遇到:git bash报错:Permission denied, please try again。经排查是ssh没有配置我的key,所以就借着这篇文章整理了一下…

典型的ETL使用场景

典型的ETL使用场景 ETL( Extract,Transform,Load)是一种用于数据集成和数据转换的常用技术。它主要用于从多个数据源中提取数据,对数据进行清洗、转换和整合,最后加载到目标系统中。ETL 的使用场景非常广泛,下面将介绍…

Windows系统Java开发环境安装

总结一下Java软件开发工程师常见的环境的安装,仅限Windows环境。 以下下载链接均来自官网,网络条件自己克服。 目录 1. JDKJDK Oracle 官网下载地址配置系统环境变量 2. Mavenapache maven 官网地址本地仓库和中央仓库配置配置系统环境变量 3. GitGit 官…

【Docker一】Docker架构、镜像操作和容器操作

一、docker基本管理和概念 1、概念 docker:开源的应用容器引擎。基于go语言开发的。运行在Linux系统中的开源的轻量级的“虚拟机” docker的容器技术可用在一台主机上轻松到达为任何应用创建一个轻量级到的,可移植的,自给自足的容器 dock…

二维码智慧门牌管理系统:提升管理效率

文章目录 前言一、快速准确录入:提高工作效率二、多样化支付:提供高效支付功能三、智能化管理:提高效率与准确性 前言 科技时代的必备工具 在当今科技高速发展的时代,二维码智慧门牌管理系统已成为各行业提高管理效率和准确性的重…

智能仪表板DevExpress Dashboard v23.1 - 支持自定义样式创建

使用DevExpress Analytics Dashboard,再选择合适的UI元素(图表、数据透视表、数据卡、计量器、地图和网格),删除相应参数、值和序列的数据字段,就可以轻松地为执行主管和商业用户创建有洞察力、信息丰富的、跨平台和设…

C语言——字符函数和字符串函数(一)

📝前言: 这篇文章对我最近学习的有关字符串的函数做一个总结和整理,主要讲解字符函数和字符串函数(strlen,strcpy和strncpy,strcat和strncat)的使用方法,使用场景和一些注意事项&…

gin投票项目5

对应视频V3版本 1.优化用户注册的功能 增加扩展字段 1.增加一个UUID字段,vachar(50)。 2.增加一个UUID的唯一索引。 UUID具有全局唯一性; 方法:在数据库中新建一个列,名为uuid并移至主键下方&#xf…

CRM系统选择技巧,什么样的CRM系统好用?

SaaS行业发展迅速,更多的企业逐渐选择CRM管理系统。打开搜索引擎,有非常多的结果。怎样在数十万个搜索结果中选择适合您的CRM系统?下面我们将聊聊,怎样选择CRM系统。 第一步:明确自身需求 重要性:每家企业…

POJ1182 食物链(并查集)

题目展示 Description 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。 现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。 有人用…

【Linux】探索Linux进程状态 | 僵尸进程 | 孤儿进程

最近,我发现了一个超级强大的人工智能学习网站。它以通俗易懂的方式呈现复杂的概念,而且内容风趣幽默。我觉得它对大家可能会有所帮助,所以我在此分享。点击这里跳转到网站。 目录 一、进程状态1.1运行状态1.2阻塞状态1.3挂起状态 二、具体L…

在UE中使用Python设置枚举类属性值的问题

目标 在UE编辑器中使用Python设置枚举类属性值会遇到些问题,本篇记录了这些问题的解决方法。 1. 设置数值类属性值 先在编辑器中选择一个Actor,然后运行下面Python代码: actor unreal.EditorLevelLibrary.get_selected_level_actors()[0…

【JavaEE】线程池

作者主页:paper jie_博客 本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。 本文于《JavaEE》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造&…

2024年网络安全竞赛-Web安全应用

Web安全应用 (一)拓扑图 任务环境说明: 1.获取PHP的版本号作为Flag值提交;(例如:5.2.14) 2.获取MySQL数据库的版本号作为Flag值提交;(例如:5.0.22) 3.获取系统的内核版本号作为Flag值提交;(例如:2.6.18) 4.获取网站后台管理员admin用户的密码作为Flag值提交…

我的隐私计算学习——隐私集合求交(1)

笔记内容来自多本书籍、学术资料、白皮书及ChatGPT等工具,经由自己阅读后整理而成。 (一)PSI的介绍 隐私计算关键技术:隐私集合求交(PSI)原理介绍 隐私计算关键技术:隐私集合求交&#xff08…

【基于Flask、MySQL和Echarts的热门游戏数据可视化平台设计与实现】

基于Flask、MySQL和Echarts的热门游戏数据可视化平台设计与实现 前言数据获取与清洗数据集数据获取数据清洗 数据分析与可视化数据分析功能可视化功能 创新点结语 前言 随着游戏产业的蓬勃发展,了解游戏销售数据对于游戏从业者和游戏爱好者都至关重要。为了更好地分…

【Python数据结构与算法】—— 搜索算法 | 期末复习不挂科系列

​ 🌈个人主页: Aileen_0v0🔥系列专栏: 数据结构与算法💫个人格言:"没有罗马,那就自己创造罗马~" 这篇博客主要探索的是计算机科学常见问题---搜索算法 “时间紧,任务重!” 话不多说,开始今天…

高工氢电年会 | 未势能源解超朋博士受邀出席并做主题演讲

12月4日,以“战略重构 商业觉醒”为主题的2023高工氢电年会在深圳举办,未势能源副总裁解超朋博士受邀出席开幕式论坛,以《把握机遇、直面挑战,迎接氢车规模化推广时代》为主题发表演讲,并参与圆桌论坛研讨。 氢势已来&…