红 黑 树

news2024/12/23 18:29:44

文章目录

  • 一、红黑树的概念
  • 二、红黑树的实现
    • 1. 红黑树的存储结构
    • 2. 红黑树的插入

一、红黑树的概念

在 AVL 树中删除一个结点,旋转可能要持续到根结点,此时效率较低

红黑树也是一种二叉搜索树,通过在每个结点中增加一个位置来存储红色或黑色,并对结点的着色进行限制,使得该二叉搜索树的最长路径不超过最短路径的两倍,即红黑树是一颗近似平衡的二叉搜索树,他不像 AVL 树的平衡那么严格,所以红黑树在插入和删除时,也不需要大量的旋转,并且搜索效率差不了 AVL 多少

红黑树是一颗二叉搜索树并且满足如下规则:

  • 每个节点不是红色就是黑色
  • 根结点是黑色的
  • 每个红结点的左右孩子一定是黑色
  • 从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点
  • 叶结点都是黑色的(这里的叶结点指的是空节点)

根据上述规则可以得到:最短路径:全黑结点的路径,最长路径:一黑一红的路径,所以红黑树可以保证最长路径不超过最短路径的一半

在这里插入图片描述

二、红黑树的实现

1. 红黑树的存储结构

// 结点的颜色
enum Color { RED, BLACK };

// 红黑树的结点
template<class K, class V>
struct RBTreeNode
{
	std::pair<K, V> _kv;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	Color _color;	// 结点的颜色

	RBTreeNode<K, V>(const std::pair<K, V>& kv = std::pair<K, V>(K(), V()))
		: _kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _color(RED)	// 为了方便树的结构调整,新结点默认为红色
	{}
};

// 红黑树
template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
	RBTree<K, V>()
		: _root(nullptr)
	{}

private:
	Node* _root;
};

2. 红黑树的插入

首先按照二叉搜索树的方式插入结点,保证插入结点之后还是二叉搜索树,为了方便树的结构调整,插入结点默认为为红色,当插入结点完成之后,可能会违反红黑树的性质,此时有三种情况

  • 插入结点的父节点是黑色:没有违反红黑树的性质

  • 插入结点的父节点是红色,叔节点存在且为红:违反了红黑树的性质,此时需要对父节点和爷爷结点进行变色

由于父节点是红色的,所以爷爷结点一定存在且为黑,变色完之后,如果 g 结点是根结点,则将 g 结点变为黑色,否则将 g 结点所在的子树当做新插入的结点,继续向上调整

在这里插入图片描述

  • 插入结点的父节点是红色,叔节点不存在或存在且为黑:违反了红黑树的性质,此时需要对爷爷结点所在的子树进行旋转然后再对结点进行变色

由于父节点是红色的,所以爷爷结点一定存在且为黑,变色完之后,子树的根结点是黑色的,不用继续向上调整

在这里插入图片描述

在这里插入图片描述

u 存在且为黑的情况,一定是由 u 存在且为红的情况继续向上调整而来的

// 右旋
void RotateR(Node* parent)
{
	Node* pparent = parent->_parent;
	Node* subL = parent->_left;
	Node* subLR = subL->_right;

	parent->_left = subLR;
	if (subLR) subLR->_parent = parent;

	subL->_right = parent;
	parent->_parent = subL;

	if (pparent == nullptr) _root = subL;
	else
	{
		if (pparent->_left == parent) pparent->_left = subL;
		else pparent->_right = subL;
	}
	subL->_parent = pparent;
}

// 左旋
void RotateL(Node* parent)
{
	Node* pparent = parent->_parent;
	Node* subR = parent->_right;
	Node* subRL = subR->_left;

	parent->_right = subRL;
	if (subRL) subRL->_parent = parent;

	subR->_left = parent;
	parent->_parent = subR;

	if (pparent == nullptr) _root = subR;
	else
	{
		if (pparent->_left == parent) pparent->_left = subR;
		else pparent->_right = subR;
	}
	subR->_parent = pparent;
}
		
// 插入
bool Insert(const std::pair<K, V>& kv)
{
	// 按照二叉搜索树的方式插入结点,保证该树插入结点之后还是二叉搜索树
	if (_root == nullptr)
	{
		_root = new Node(kv);
		_root->_color = BLACK;
		return true;
	}

	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_kv.first > kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_kv.first < kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else return false;
	}

	cur = new Node(kv);
	if (parent->_kv.first > kv.first) parent->_left = cur;
	else parent->_right = cur;

	cur->_parent = parent;

	// 更新颜色
	while (parent && parent->_color == RED)
	{
		Node* grandfather = parent->_parent;
		if (grandfather->_left == parent)
		{
			Node* uncle = grandfather->_right;
			
			// u 存在且为红
			// u 不存在或存在且为黑
			//		p 为 g 的左,cur 为 p 的左 右单旋
			//		p 为 g 的左,cur 为 p 的右 先左旋再右旋
			if (uncle && uncle->_color == RED)
			{
				grandfather->_color = RED;
				parent->_color = BLACK;
				uncle->_color = BLACK;

				// 继续判断是否违反了红黑树的性质
				cur = grandfather;
				parent = grandfather->_parent;
			}
			else
			{
				if (parent->_left == cur)
				{
					RotateR(grandfather);
					grandfather->_color = RED;
					parent->_color = BLACK;
				}
				else
				{
					RotateL(parent);
					RotateR(grandfather);
					grandfather->_color = RED;
					cur->_color = BLACK;
				}
			}
		}
		else
		{
			Node* uncle = grandfather->_left;

			// u 存在且为红
			// u 不存在或存在且为黑
			//		p 为 g 的右,cur 为 p 的右 左单旋
			//		p 为 g 的右,cur 为 p 的左 先右旋再左旋
			if (uncle && uncle->_color == RED)
			{
				grandfather->_color = RED;
				parent->_color = BLACK;
				uncle->_color = BLACK;

				// 继续判断是否违反了红黑树的性质
				cur = grandfather;
				parent = grandfather->_parent;
			}
			else
			{
				if (parent->_right == cur)
				{
					RotateL(grandfather);
					grandfather->_color = RED;
					parent->_color = BLACK;
				}
				else
				{
					RotateR(parent);
					RotateL(grandfather);
					grandfather->_color = RED;
					cur->_color = BLACK;
				}
			}
		}
	}

	_root->_color = BLACK;
	return true;
}

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

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

相关文章

软件测试缺陷报告详解

【软件测试行业现状】2023年了你还敢学软件测试&#xff1f;未来已寄..测试人该何去何从&#xff1f;【自动化测试、测试开发、性能测试】 缺陷报告是描述软件缺陷现象和重现步骤地集合。软件缺陷报告Software Bug Report&#xff08;SBR&#xff09;或软件问题报告Software Pr…

【开发篇】二、属性绑定与校验

文章目录 1、ConfigurationProperties自定义Bean属性绑定2、EnableConfigurationProperties注解3、ConfigurationProperties第三方Bean属性绑定4、松散绑定5、常用计量单位6、数据校验7、yaml绑定值的坑--关于进制 1、ConfigurationProperties自定义Bean属性绑定 前面读取yaml…

gateway之过滤器(Filter)详解

文章目录 什么是过滤器过滤器的种类局部过滤器代码示例全局过滤器代码示例 总结 什么是过滤器 在Spring Cloud中&#xff0c;过滤器&#xff08;Filter&#xff09;是一种关键的组件&#xff0c;用于在微服务架构中处理和转换传入请求以及传出响应。过滤器位于服务网关或代理中…

CRM客户管理系统主要用途

对于大多数企业而言业绩就是生命线&#xff0c;因此销售环节在企业管理过程中意义重大。面对愈发内卷的市场竞争企业就要借助CRM销售管理系统改善各个环节存在的漏洞&#xff0c;占据优势。那么&#xff0c;销售管理系统的用途有哪些&#xff0c;接下来我们从下面3个功能来介绍…

ESP32-IDF使用I2S驱动MAX98375--解析WAV文件

一. 简介 本篇文章将介绍如何使用ESP32S3通过I2S发送WAV音频数据&#xff0c;驱动MAX98375A进行音频的播放。是EVE_V2项目开发的一部分工作。 二. MAX98375A介绍 芯片特性如下&#xff0c;可以在芯片手册上找到。 单电源工作(2.5V至5.5V)3.2W输出功率&#xff1a;4Ω&#…

anaconda安装完成之后输入conda -V没有反应

anaconda安装完成后&#xff0c;conda没有反应 vim ~/.bashrc后面添加内容 # added by Anaconda3 5.3.0 installer # >>> conda init >>> # !! Contents within this block are managed by conda init !! __conda_setup"$(CONDA_REPORT_ERRORSfalse /u…

springboot实战(七)之jackson配置前后端交互下划线转驼峰对象序列化与反序列化

目录 环境&#xff1a; 1.驼峰转下划线配置 1.1单个字段命名转化使用JsonProperty注解 1.2单个类进行命名转化使用JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)注解 3.全局命名策略配置 2. 序列化以及反序列化 2.1序列化 2.2反序列化 3.自定义序列…

ImageJ查看图像灰度值矩阵及像素编号从0开始

ImageJ查看图像灰度值矩阵 imagej打开一幅图像 然后image —— transform——image to results&#xff0c;等一下就会出现灰度值矩阵 我读取的如下&#xff0c;可以看出&#xff0c;imagej对像素的编号是从0开始的&#xff0c;切记&#xff01;&#xff01;&#xff01;跟C/…

【论文笔记】Scene as Occupancy

原文链接&#xff1a;https://arxiv.org/abs/2306.02851 1. 引言 与传统的3D框物体表达相比&#xff0c;使用3D占用表达是几何感知的&#xff0c;因为3D框表达简化了物体的形状。此外&#xff0c;现有基于视觉的方法很少考虑时间信息&#xff1b;单阶段方法缺少从粗到细的细化…

Linux 线程(thread)

进程线程区别 创建线程 #include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); -功能&#xff1a;创建一个子线程&#xff0c;一般情况下main函数所在的线程称为主线程&#xff0c;…

科技资讯|Canalys发布全球可穿戴腕带设备报告,智能可穿戴增长将持续

市场调查机构 Canalys 近日发布报告&#xff0c;表示 2023 年第 2 季度全球可穿戴腕带设备出货量达 4400 万台&#xff0c;同比增长了 6%。 主要归功于其亲民的价格以及消费者对价位较高的替代品仍持谨慎态度&#xff0c;基础手环市场尽管与去年同期相比有所下降&#xff0c;…

JDBC连接mysql

文章目录 JDBC简介JDBC-MYSQL驱动下载JDBC使用通过结果集查询 PreparedStatement 预处理查询事务批处理连接池1.C3P02.德鲁伊 druid DBUtils工具 JDBC简介 JDBC(Java Data Connectivity,java数据库连接)是一种用于执行sql语句的JavaAPI&#xff0c;可以为多种关系数据库提供统…

极简式看图软件 Pixea Plus for Mac

Pixea Plus for Mac介绍 简单易用&#xff1a;Pixea的用户界面非常简洁直观&#xff0c;容易上手。 图片管理&#xff1a;Pixea允许用户创建个人或业务相册&#xff0c;并提供了图片搜索、排序、过滤等多种管理功能&#xff0c;方便用户组织和查找照片。 图片编辑&#xff1…

【Java 基础篇】Java网络编程:文件下载详解

文件下载是网络应用程序中的一个常见任务&#xff0c;允许用户从远程服务器获取文件。Java提供了丰富的网络编程库&#xff0c;使文件下载变得相对简单。本文将详细介绍如何使用Java进行文件下载&#xff0c;并提供一些相关内容的解释。 什么是文件下载 文件下载是指从一个网…

Django之初入门

一&#xff09;Django简介 1.简介 Django是一个开源的Python Web框架&#xff0c;它以简洁高效的方式帮助开发者构建复杂的Web应用程序。Django采用了MVC&#xff08;Model-View-Controller&#xff09;的架构模式&#xff0c;通过强大的工具和功能&#xff0c;提供了一套完整…

最新版本 在线生成SSL证书源码 后台对接ssl证书api接口

SSL证书是保证网络安全的基本保障之一。向您介绍我们的在线生成SSL证书系统&#xff0c; 支持在线生成SSL证书系统&#xff0c;用户登录可在线申请SSL&#xff0c;后台对接ssl证书api接口。

金融行业如何利用软文营销进行品牌维护

金融行业作为强监管行业&#xff0c;与人民财产息息相关&#xff0c;因此塑造可靠的品牌形象是金融行业必须面对的课题&#xff0c;而金融行业的特性决定了它在品牌传播与建设上会受到一定的限制&#xff0c;因此金融行业想要进行品牌维护的话&#xff0c;软文营销就是非常不错…

频频刷屏朋友圈,白酒如何越来越年轻化?来聊聊白酒企业数字化

最近&#xff0c;某白酒品牌频频吸引大众眼球&#xff0c;白酒与咖啡、巧克力等联名衍生品一经推出便掀起热潮。某商品由于太过火爆&#xff0c;甚至一度售罄下架。 不得不说&#xff0c;我国拥有超大规模内需市场&#xff0c;消费潜力巨大。 当前&#xff0c;创新消费场景加…

vue 路由中 vite 与webpack 动态 导入的方法汇总

vite 动态导入路由&#xff1a; router/index.js文件 //vite 下面寻找 views 文件夹下面所有的page.js文件 let pageList import.meta.glob(../views/**/page.json, {eager: true,import: default, }); //所有的文件 const modules import.meta.glob(../views/**/**.vue);/…

el-select 下拉框全选、多选的几种方式组件

组件一、基础多选 适用性较广的基础多选&#xff0c;用 Tag 展示已选项 <template><el-select v-model"value1" multiple placeholder"请选择"><el-optionv-for"item in options":key"item.value":label"item.la…