当前位置: 首页 > news >正文

数据结构:AVL树——C++实现(待补充)

前言

AVL树特点:在BST树的基础上,引入了节点“平衡”的概念,任意一个节点的左右子树高度差不超过1。

为什么要引入平衡这个概念?
在BST树中,由于插入后就是排序好的,那就会存在这样一种情况:如果插入的元素依次增大,就会导致理想中的二叉树,形成了一个链表,如下图,这种情况会导致增删查的时间复杂度并不是O(logn),而是O(n)。为了解决这样情况,引入了平衡的概念。

在这里插入图片描述

文章目录

  • 前言
  • AVL树节点的定义
  • AVL树维护节点平衡的四种旋转操作
    • 1. 右旋转-左孩子的左子树太高
    • 2. 左旋转-右孩子的右子树太高
    • 3. 左平衡-左孩子的右子树太高
    • 4. 右平衡-右孩子的左子树太高
  • AVL树的增加-insert()
  • AVL树的删除(待补充)

AVL树节点的定义

template<typename T>
class AVLTree
{
private:
	// 定义AVL树节点类型
	struct Node
	{
		Node(T data = T())
			: val_(data)
			, left_(nullptr)
			, right(nullptr)
			, height_(1)
		{}

		T val_;
		Node* left_;
		Node* right_;
		int height_;	// 记录节点高度值
	};

	Node* root_;	// 指向根节点
};

AVL树维护节点平衡的四种旋转操作

1. 右旋转-左孩子的左子树太高

在这里插入图片描述

操作步骤:right rotate

  1. child指针指向node节点的左孩子;
  2. child如果有右子树,那就让node的左子树指向child的右子树;
  3. child的右子树指向node;
  4. 更新node和child节点的高度值。

代码:

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

// 右旋转操作,以参数node为轴做右旋转操作,并把新的根节点返回
Node* rightRotate(Node* node)
{
	Node* child = node->left_;
	node->left_ = child->right_;
	child->right_ = node;
	// 高度更新
	node->height_ = std::max(height(node->left_), height(node->right_)) + 1;
	child->height_ = std::max(height(child->left_), height(child->right_)) + 1;
	// 返回旋转后的子树新的根节点
	return child;
}

2. 左旋转-右孩子的右子树太高

在这里插入图片描述

操作步骤:left rotate

  1. child指针指向node的右孩子;
  2. 让node的右子树指向child的左子树;
  3. child的左子树指向node;
  4. 更新node和child节点的高度值。

代码:

// 左旋转操作
Node* leftRotate(Node* node)
{
	Node* child = node->right_;
	node->right_ = child->left_;
	child->left_ = node;
	// 高度更新
	node->height_ = std::max(height(node->left_), height(node->right_)) + 1;
	child->height_ = std::max(height(child->left_), height(child->right_)) + 1;
	// 返回旋转后的子树新的根节点
	return child;
}

3. 左平衡-左孩子的右子树太高

左平衡,即左-右旋转
可直接调用前两个接口

在这里插入图片描述

代码:

// 左平衡操作
Node* leftBalance(Node* node)
{
	// 先对左孩子进行左旋转
	node->left_ = leftRotate(node->left_);
	// 右旋转
	return rightRotate(node);
}

4. 右平衡-右孩子的左子树太高

右-左旋转
可直接调用前两个接口
在这里插入图片描述

代码:

// 右平衡操作
Node* rightBalance(Node* node)
{
	// 先对右孩子进行右旋转
	node->right_ = rightRotate(node->right_);
	// 左旋转
	return leftRotate(node);
}

AVL树的增加-insert()

先来看看BST树的递归插入操作

如果node为nullptr,表示找到插入的位置了;
如果当前节点的值等于data,直接返回;
如果当前节点的值小于data,则递归往左子树走;
如果当前节点的值大于data,则递归往右子树走。

// 递归插入操作
Node* insert(Node* node, const T& data)
{
	if (node == nullptr)
	{
		// 递归结束,找到插入data的位置,生成新节点返回
		return new Node(data);
	}
	if (node->val_ == data)
	{
		return node;
	}
	else if (node->val_ < data))
	{
		node->right_ = insert(node->right_, data);
	}
	else
	{
		node->left_ = insert(node->left_, data);
	}
	return node;
}

AVL树的插入操作:相对于BST树增加了哪些内容?
如果node为nullptr,表示找到插入的位置了,递归结束,申请新节点返回;

  1. 如果当前节点值大于data,则递归往左子树插入;在回溯时判断节点是否失衡,因为是在左子树插入,所以判断左子树失衡问题。若失衡,则有两种情况,一是左孩子的左子树太高,则对node进行右旋转;二是左孩子的右子树太高,则对node做左平衡(左-右旋转)。
  2. 如果当前节点值小于data,则递归往右子树插入;在回溯时判断节点是否失衡,因为是在右子树插入,所有判断右子树失衡问题。若失衡,则又有两种情况,一是右孩子的右子树太高,则对node进行左旋转;二是右孩子的左子树太高,则对node做右平衡(右-左旋转)。
  3. 如果当前节点值等于data,表示遇到相同的值,什么也不做;
  4. 退出这三个判断后,在回溯过程中修正节点的高度,将其修正为左子树和右子树当中最高的高度再加1。
  5. 最后返回当前节点。
// 插入操作实现
Node* insert(Node* node, const T& data)
{
	if (node == nullptr) // 递归结束
	{
		return new Node(data);
	}

	if (node->val_ > data)
	{
		node->left_ = insert(node->left_, data);
		// 添加1 在递归回溯时判断节点是否失衡
		// node的左子树太高,左子树失衡
		if (height(node->left_) - height(node->right_) > 1)
		{
			// 两种情况:左孩子左子树太高,节点失衡
			if (height(node->left_->left_) >= height(node->left_->right_))
			{
				node = rightRotate(node);
			}
			else // 左孩子的右子树太高,做左平衡
			{
				node = leftRotate(node);
			}
		}
	}
	else if (node->val_ < data)
	{
		node->right_ = insert(node->right_, data);
		// 添加2 在递归回溯时判断节点是否失衡
		// node的右子树太高,右子树失衡
		if (height(node->right_) - height(node->left_) > 1)
		{
			// 两种情况:右孩子右子树太高,节点失衡
			if (height(node->right_->right_) >= height(node->right_->left_))
			{
				node = leftRotate(node);
			}
			else // 右孩子的左子树太高,做右平衡
			{
				node = rightRotate(node);
			}
		}
	}
	else
	{
		; // 找到相同节点了,直接返回
	}

	// 添加3 添加节点后,回溯过程中检测更新节点高度
	node->height_ = max(height(node->left_), height(node->right_)) + 1;

	return node;
}

AVL树的删除(待补充)

相关文章:

  • Opencv之频率域滤波
  • 海思3559万能平台搭建:OSD功能的优化
  • 从1到100这100个自然数中任取10个数,使他们的倒数和等于1。这10个数分别是多少?
  • 【香橙派4B】6、测试串口
  • 【408】【数据结构】【图】
  • 【架构设计】如何实现3ms内从1000w级别的用户里面随机抽奖出100名用户
  • HTB-Chatterbox
  • 矩阵乘法的消去律
  • FL Studio最新20.9版本完整FL水果中文语言更新
  • JAVA集合(二)List接口详解
  • 矩阵的秩的性质
  • Redis在SpringBoot项目中使用
  • Android AIDL跨进程通信
  • Java大牛必会|分布式缓存实现方案之Spring Cache
  • KF、EKF、IEKF、UKF卡尔曼滤波器
  • AngularJS指令开发(1)——参数详解
  • Docker容器管理
  • iOS筛选菜单、分段选择器、导航栏、悬浮窗、转场动画、启动视频等源码
  • Java反射-动态类加载和重新加载
  • node入门
  • PHP 小技巧
  • python 学习笔记 - Queue Pipes,进程间通讯
  • Redis字符串类型内部编码剖析
  • Vue2.0 实现互斥
  • vue-cli在webpack的配置文件探究
  • 闭包,sync使用细节
  • 搭建gitbook 和 访问权限认证
  • 基于遗传算法的优化问题求解
  • 为什么要用IPython/Jupyter?
  • 源码之下无秘密 ── 做最好的 Netty 源码分析教程
  • gunicorn工作原理
  • 扩展资源服务器解决oauth2 性能瓶颈
  • ​HTTP与HTTPS:网络通信的安全卫士
  • ​LeetCode解法汇总2670. 找出不同元素数目差数组
  • #LLM入门|Prompt#2.3_对查询任务进行分类|意图分析_Classification
  • #数学建模# 线性规划问题的Matlab求解
  • (八)c52学习之旅-中断实验
  • (笔试题)合法字符串
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致
  • (免费领源码)Java#ssm#MySQL 创意商城03663-计算机毕业设计项目选题推荐
  • (原+转)Ubuntu16.04软件中心闪退及wifi消失
  • (转)socket Aio demo
  • .[hudsonL@cock.li].mkp勒索病毒数据怎么处理|数据解密恢复
  • .net 8 发布了,试下微软最近强推的MAUI
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .NET Core 成都线下面基会拉开序幕
  • .NET LINQ 通常分 Syntax Query 和Syntax Method
  • .Net Memory Profiler的使用举例
  • .NET的微型Web框架 Nancy
  • .net解析传过来的xml_DOM4J解析XML文件
  • .NET连接MongoDB数据库实例教程
  • ::什么意思
  • [ JavaScript ] JSON方法
  • [Android Pro] android 混淆文件project.properties和proguard-project.txt
  • [AutoSar]BSW_OS 01 priority ceiling protocol(PCP)