数据结构与算法_AVL平衡二叉树_四种旋转,插入和删除

news2025/1/20 20:03:15

1 AVL平衡二叉树的概念

平衡二叉树在BST树基础上加了平衡操作。
BST树特点 :在BST树的基础上,引入了节点“平衡”的概念,任意一个节点的左右子树高度差不超过 1 ,为了维持节点的平衡,引入了四种旋转操作,如下:
1.左孩子左子树太高,做右旋转操作
2.右孩子的右子树太高,做左旋转操作
3.左孩子的右子树太高,做左-右旋转操作(也叫左平衡操作)
4.右孩子的左子树太高,做右-左旋转操作(也叫右平衡操作)

AVL树:记忆的时候,与BST树的插入删除联系起来。
下面分别记录AVL的四种失衡操作,并举例说明。

2 AVL的四种旋转操作

2.1 右旋转

右旋转调整。如下图所示,40左子树高度为2,右子树高度为0 ,这种情况称为左失衡,需要进行右旋转。
在这里插入图片描述

2.2 左旋转

左旋转调整。当节点的右子树高度大于节点的左孩子高度时候,如下图情况,需要做左旋转调整。
调整方法:先记录当前节点的右孩子。然后,当前节点的右孩子指向当前节点孩子的左孩子;然后孩子节点的左孩子指向当前节点。
在这里插入图片描述

2.3 左右旋转

左右旋转,也叫左平衡调整。这种情况需要调整两次,先进行一次左旋转,再进行一次右旋转。如下图所示。
在这里插入图片描述

2.4 右左旋转

右左旋转,也称为有平衡操作。这种情况也需要调整两次,即先进行一次右旋转,再进行一次左旋转。
在这里插入图片描述

3 AVL的插入,旋转调整举例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. BST树 和 AVL树插入1-10后,对比

BST树插入1-10后,BST树变成了一个链表;AVL树则是一颗平衡二叉树。
在这里插入图片描述

5. AVL四种旋转操作,插入,删除代码实现

#include <iostream>
#include <functional>
using namespace std;

// 定义AVL节点类型 
template <typename T>
class AVLTree
{
public:
	AVLTree() : root_(nullptr) { }

	// 插入操作
	void insert(const T &val)
	{
		root_ = insert(root_, val);
	}

	// 删除操作
	void remove(const T & val)
	{
		root_ = remove(root_, val);
	}


private:
	// 定义AVL树节点类型 
	struct Node
	{
		Node(T data = T())
			:data_(data)
			, left_(nullptr)
			, right_(nullptr)
			, height_(1)
		{}
		T data_;
		Node *left_;
		Node *right_;
		int height_;
	};
	Node *root_;

	//
	int max(int a, int b)
	{
		return a > b ? a : b;
	}


	/*
	*	功能:返回node高度
	*/
	int height(Node *node)
	{
		return node == nullptr ? 0 : node->height_;
	}

	/* 
	*	功能:node节点进行右旋转,并返回旋转后的根节点
	*
	*	参数:
	*		 node : 当前节点
	*	
	*   返回值: 
	*		 返回旋转后的根节点
	*/
	Node *rightRotate(Node *node)
	{
		Node *child = node->left_;
		node->left_ = child->right_;
		child->right_ = node;

		// 旋转后根节点和child节点发生了变化,所以要更新高度值。
		node->height_ = max(height(node->left_), height(node->right_)) + 1;
		node->height_ = max(height(child->left_), height(child->right_)) + 1;

		// 返回旋转后的根节点
		return child;
	}

	/*  功能:左旋转,并返回旋转后的根节点
	*
	*	参数:
	*		 node : 根节点
	*
	*   返回值:
	*		 返回旋转后的根节点
	*/
	Node *leftRotate(Node *node)
	{
		Node *child = node->right_;
		node->right_ = child->left_;
		child->left_ = node;

		// 更新旋转后的高度值
		node->height_ = max(height(node->left_), height(node->right_)) + 1;
		child->height_ = max(height(child->left_), height(child->right_)) + 1;

		// 返回旋转后的根节点
		return child;
	}

	/*
	*	功能:左右旋转,并把根节点返回
	*
	*	参数:
	*		  node : 以参数node为旋转轴	
	*/		
	Node * leftBalance(Node *node)
	{
		node->left_ = leftRotate(node->left_);  // 先对根节点左孩子进行左旋转
		return rightRotate(node);				// 再对,根节点右孩子进行右旋转。
	}

	/*
	*	功能:右左旋转,并把根节点返回
	*
	*	参数:
	*		  node : 以参数node为旋转轴
	*/
	Node * rightBalance(Node *node)
	{
		node->right_ = rightRotate(node->right_);  // 以左孩子为根进行左旋
		return leftRotate(node);
	}

	/*
	*	功能:node中插入节点,并返回新插入节点
	*
	*/
	Node * insert(Node *node, const T & val)
	{
		if (node == nullptr)
		{
			return new Node(val);
		}

		if (node->data_ > val)   // 向当前节点的左子树中插入新节点,所以要判断,当前节点的左子树是否太高
		{
			node->left_ = insert(node->left_, val);  // 新插入的节点都在叶子节点上,所以下面回溯时,调整树的平衡状态。

			// 在回溯时候,判断节点是否失衡,如果node的左子树太高导致Node失衡了,
			// 当前在向左子树中插入节点,新节点在左子树的叶子中,只可能导致左子树失衡,不存在右子树失衡的情况,所以直接用左子树高度 减去 右子树高度。
			if (height(node->left_) - height(node->right_) > 1)  // 如果当前节点的左子树比右子树高
			{		 // 分两种情况来判断,一个是左孩子的左子树节点失衡,一个树右孩右子树节点失衡。
				if (height(node->left_->left_) >= height(node->left_->right_))
				{
					node = rightRotate(node);   // 用旋转后的根节点 代替根节点
				}
				else
				{
					node = leftBalance(node);
				}
			}
		}
		else if (node->data_ < val)
		{
			node->right_ = insert(node->right_, val);

			// 在递归回溯时候,判断节点是否失衡,Node的右子树太高,node 失衡了
			if (height(node->right_) - height(node->left_) > 1)
			{
				if (height(node->right_->right_) >= height(node->right_->left_))  // 1-10,插入节点9时会出现这种情况
				{
					node = leftRotate(node);
				}
				else
				{
					node = rightBalance(node);
				}

			}
		}
		else // 如果新插入节点与当前节点相等,不插入,不用再递归,直接回溯
		{
			;
		}
		
		// 更新节点高度。因为递归中增加了新节点,所以回溯时候,更新节点的高度。
		node->height_ = max(height(node->left_), height(node->right_)) + 1;

		return node; // rrturn后,直接回溯,不再向下递归
	}

	/*
	*	功能:删除val节点
	*
	*	参数:
	*		  node: 当前处理的根节点
	*		  val : 待删除的值
	*/
	Node * remove(Node *node,const T & val)
	{
		if (node == nullptr)
		{
			return nullptr;
		}

		if (node->data_ > val)    // 如果当前节点大于val,则从当前节点左孩子中查找
		{
			node->left_ = remove(node->left_, val);
			
			// 删除左子树的节点之后,可能造成右子树太高
			if (height(node->right_) - height(node->left_) > 1)
			{
				// 右子树太高,分两种情况,第一:
				if (height(node->right_->right_) >= height(node->right_->left_))
				{
					// 右孩子的右子树太高
					node = leftRotate(node);
				}
				else
				{
					node = rightBalance(node);
				}
			}

		}
		else if (node->data_ < val)
		{
			node->right_ = remove(node->right_, val);

			// 删除右节点后,可能造成左子树太高
			if (height(node->left_) - height(node->right_) > 1)
			{
				// 如果左孩子的左子树失衡
				if (height(node->left_->left_) >= height(node->left_->right_))
				{
					node = rightRotate(node);
				}
				else
				{
					node = leftBalance(node);
				}
			}

		}
		else  // 删除节点,先删除两个孩子的节点,再删除一个孩子的节点
		{
			 // 找到后,先处理两个孩子的节点,然后再删除一个孩子的节点。
			if (node->left_ != nullptr && node->right_ != nullptr)
			{
				// 为了避免删除前驱和后继节点造成节点失衡,she高删除谁
				if (height(node->left_) >= height(node->right_))
				{
					// 删除	前驱
					Node *pre = node->left_;
					while (pre->right_ != nullptr)
					{
						pre = pre->right_;
					}
					node->data_ = pre->data_;						// 覆盖值
					node->left_ = remove(node->left_, pre->data_);  // 删除前驱节点
				}
				else
				{
					// 删除后继节点
					Node *post = node->right_;
					while (post->right_ != nullptr)
					{
						post = post->left_;
					}
					node->data_ = post->data_;
					node->right_ = remove(node->right_, post->data_);  // 删除后继节点
				}
			}
			else  // 最多有一个孩子节点
			{
				if (node->left_ != nullptr)
				{
					Node *left = node->left_;
					delete node;
					return left;
				}
				else if (node->right_ != nullptr)
				{
					Node *right = node->right_;
					delete node;
					return right;
				}
				else
				{
					return nullptr;
				}
			}

		}

		// 更新节点高度
		node->height_ = max(height(node->left_), height(node->right_)) + 1;
		return node;
	}
};


int main()
{
	AVLTree<int> avl;
	for (int i = 1; i < 11; i++)
	{
		avl.insert(i);
	}

	avl.remove(6);
	system("pause");
	return 0;
}

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

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

相关文章

MySQL的时区引起的前后端数据交互不畅的问题解决

MySQL的时区问题 一、问题起源 在使用swagger2进行代码测试时&#xff0c;执行完成后显示的时间与国内时间少了8个小时 强迫症的原因&#xff0c;就手贱了如下操作 ① 修改MySQL内的时间 set global time_zone 8:00; flush privileges;② show variables like “%time_zone%…

整数除法不用除号

给定两个整数 a 和 b &#xff0c;求它们的除法的商 a/b &#xff0c;要求不得使用乘号 *、除号 / 以及求余符号 % 。 注意&#xff1a; 整数除法的结果应当截去&#xff08;truncate&#xff09;其小数部分&#xff0c;例如&#xff1a;truncate(8.345) 8 以及 truncate(-2…

【路径规划】(2) A* 算法求解最短路,附python完整代码

大家好&#xff0c;今天和各位分享一下机器人路径规划中非常经典的 A* 算法&#xff0c;感兴趣的点个关注&#xff0c;文末有 python 代码&#xff0c;那我么开始吧。 1. 算法介绍 A* 算法是 1968 年 P.E.Hart[1]等人所提出的在全局地图环境中所有已知情形下求解最短路径问题的…

部分gcc预定义宏和函数栈帧的内存分布

本文简单基于树莓派8&#xff0c;linux4.4.50版本&#xff0c;32位arm cpu 尝试了解函数调用栈的内存分布的形态。使用gcc内置的宏 __builtin_frame_address 来打印栈帧内存上的信息&#xff0c;以及了解一下常用的gcc 内置的宏的输出。 针对 __builtin_frame_address 在gcc官网…

猴子也能学会的jQuery第十二期——jQuery遍历(上)

&#x1f4da;系列文章—目录&#x1f525; 猴子也能学会的jQuery第一期——什么是jQuery 猴子也能学会的jQuery第二期——引用jQuery 猴子也能学会的jQuery第三期——使用jQuery 猴子也能学会的jQuery第四期——jQuery选择器大全 猴子也能学会的jQuery第五期——jQuery样式操作…

PIC单片机3——外部中断

//RB2&#xff08;INT2&#xff09;作为外中断 #include <p18cxxx.h>/*18F系列单片机头文件*/ void PIC18F_High_isr(void);/*中断服务函数声明*/ void PIC18F_Low_isr(void); #pragma code high_vector_section0x8 /*高优先级中断响应时&#xff0c;会自动跳转到0x8处…

基于三相坐标系状态方程的感应电动机起动动态计算matlab程序

基于三相坐标系状态方程的感应电动机起动动态计算matlab程序 1 异步电动机动态数学模型的性质 电磁耦合是机电能量转换的必要条件&#xff0c;电流与磁通的乘积产生转矩&#xff0c;转速与磁通的乘积得到感应电动势。无论是直流电动机&#xff0c;还是交流电动机均如此。 交、直…

二十七、CANdelaStudio深入-编辑技巧(一致性检查)

本专栏将由浅入深的展开诊断实际开发与测试的数据库编辑,包含大量实际开发过程中的步骤、使用技巧与少量对Autosar标准的解读。希望能对大家有所帮助,与大家共同成长,早日成为一名车载诊断、通信全栈工程师。 本文介绍CANdelaStudio软件的一致性检查,欢迎各位朋友订阅、评论…

『LeetCode|每日一题』---->最小路径和

目录 1.每日一句 2.作者简介 『LeetCode|每日一题』最小路径和 1.每日一题 2.解题思路 2.1 思路分析 2.2 核心代码 2.3 完整代码 2.4 运行结果 1.每日一句 希望冬天的风能吹散一年里所有的遗憾 2.作者简介 &#x1f3e1;个人主页&#xff1a;XiaoXiaoChen-2716 &#x1f…

Vue3框架中CompositionAPI的基本使用(第十课)

1.Setup函数 理解&#xff1a;Vue3.0中一个新的配置项&#xff0c;值为一个函数。 setup是所有Composition API&#xff08;组合API&#xff09;“ 表演的舞台 ”。 组件中所用到的&#xff1a;数据、方法等等&#xff0c;均要配置在setup中。 setup函数的两种返回值&#x…

kubernetes工作负载之控制器

目录 ​一、概述 二、Deployment 控制器 2.1Deployment 部署应用 2.2Deployment滚动升级 2.2.1应用部署完成 2.2.2更新镜像三种方式 2.3 Deployment 发布失败回滚 2.4Deployment 水平扩容 三、DaemonSet控制器 四、Job控制器 4.1Job一次性执行 4.2定时任务&#xf…

查询:按A分组,满足B时对应的C

1.场景 这种问题我自己归纳为“找对应行”问题&#xff0c;例如有下面一场表&#xff08;学生做题&#xff0c;对每个知识点的得分情况&#xff09; 字段&#xff1a;主键id、user_id、score、is_study、knowledgeName、updateTime场景1&#xff1a;按用户分组&#xff0c;求…

Nginx (7):nginx高可用配置

所谓的高可用&#xff0c;就是虽然nginx可以反向代理&#xff0c;如果某个内部服务器down了&#xff0c;可以使用其他的内部服务器&#xff0c;然而万一nginx挂了呢&#xff1f;&#xff1f;&#xff1f;&#xff1f;布置多个nginx再反向代理nginx&#xff1f;&#xff1f;反向…

数据结构学习笔记(V):树与二叉树

目录 1 树 1.1 树的定义和基本术语 1.定义 2.基本术语 1.2 树的性质 2 二叉树 2.1 二叉树的定义和基本术语 1.定义 2.特殊二叉树 2.2 二叉树性质 2.3 二叉树存储结构 1.顺序存储 2.链式存储 3 二叉树进阶 3.1 二叉树顺序遍历 1.先序遍历 2.中序遍历 3.后序遍…

第十二周总结

这周我来总结一下数论分块和佩尔方程&#xff1a; 已知正整数n&#xff0c;求&#xff0c;对n/i下取整&#xff0c;相当于把一组数分块了&#xff0c;首先我们来找一下规律&#xff1a;n20时 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 …

现代密码学导论-20-流密码

目录 3.6 实际操作和加密方式 3.6.1 流密码 CONSTRUCTION 3.30 使用伪随机函数构造流密码 3.6.2 流密码的同步模式 CONSTRUCTION 3.31 流密码的异步模式 3.6 实际操作和加密方式 现代密码学导论-14-基于伪随机发生器的EAV安全_南鸢北折的博客-CSDN博客 CONSTRUCTION 3.17…

Spring Cloud整合Nacos集群

目录 第一章 微服务架构图 第二章 Spring Cloud整合Nacos集群 第三章 Spring Cloud GateWay 第四章 Spring Cloud Alibaba 整合Sentinel 第五章 Spring Cloud Alibaba 整合SkyWalking链路跟踪 第六章 Spring Cloud Alibaba 整合Seata分布式事务 第七章 Spring Cloud 集成Auth用…

计量数据分析数据库-Stata分析包使用指南、计量分析资料等八大数据大全

一、计量前沿stata 分析包使用指南 当考虑自变量X对因变量Y的影响时&#xff0c;如果X通过影响变量M来影响Y,则称M为中介变量&#xff08;mediator或mediating variable) (Judd & Kenny, 1981; Baron &Kenny,1986)。X通过中介变量M对Y产生的影响就是中介效应&#xff…

【深入理解C++】可调用对象、std::function、std::bind()

文章目录1.可调用对象1.1 函数指针1.2 具有operator()成员函数的类对象/仿函数/函数对象1.3 可被转换为函数指针的类对象1.4 类成员函数指针2.std::function2.1 绑定普通函数2.2 绑定类的静态成员函数2.3 绑定具有operator()成员函数的类对象2.4 绑定可被转换为函数指针的类对象…

【无标题】SAR雷达系统反设计及典型目标建模与仿真实现研究——目标生成与检测(Matlab代码实现)

&#x1f352;&#x1f352;&#x1f352;欢迎关注&#x1f308;&#x1f308;&#x1f308; &#x1f4dd;个人主页&#xff1a;我爱Matlab &#x1f44d;点赞➕评论➕收藏 养成习惯&#xff08;一键三连&#xff09;&#x1f33b;&#x1f33b;&#x1f33b; &#x1f34c;希…