红黑树底层原理

news2024/11/22 9:10:58

文章目录

  • 什么是红黑树
  • 红黑树的性质
  • 插入
    • 叔叔存在且为红
    • 叔叔存在且为黑或叔叔不存在
  • 调整总结
  • 右旋
  • 左旋
  • 旋转总结

什么是红黑树

红黑树也是一种二叉搜索树,只不过给这棵树上的节点带上了颜色,但是已经有了AVL树为什么还要搞出红黑树这个东西呢?首先AVL树和红黑树是同一量级的,查找一个值事假复杂度相同,但AVL树而言,控制严格平衡需要付出很大的代价,在插入和删除时需要进行大量的旋转

红黑树的性质

1.任意节点的颜色不是红色就是黑色
2.树中不允许父子节点之间颜色不允许同时为红
3.根节点一定为黑色
4.从某一节点到叶子节点的任意路径,黑色节点个数相同
5.任意叶子结点为黑色,这里的叶子结点是指空节点

插入

对于新插入的节点,我们默认设置它的颜色为红,若我们把它设为黑色,那么就一定会违反红黑树的规则,导致各路径黑色节点数目不相同,就一定需要调整,而设为红的,对于父亲为黑的这种情况,甚至不用对该树进行调整。注意红黑树的路径个数是到空节点的个数,空节点默认为黑色
下图树的路径个数为44

对于红黑树的插入操作,当前节点是否需要调整是看父亲节点的颜色,而要怎么进行调整则是看叔叔节点的颜色,对于违反红黑树性质的几种情况及其调整方案如下
约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

叔叔存在且为红

在这里插入图片描述
对于父亲为红,且叔叔存在且为红的情况,我们的处理方法是让父亲和叔叔都变黑,让爷爷变红,如果爷爷是根节点那么就让爷爷也变为黑,如果爷爷是子树且爷爷的父亲为红色,那么就要继续向上调整。

if (uncle && uncle->_col == RED)
{
	// 变色
	parent->_col = uncle->_col = BLACK;
	grandfather->_col = RED;

	// 继续向上处理
	cur = grandfather;
	parent = cur->_parent;
}

叔叔存在且为黑或叔叔不存在

在这里插入图片描述

如果父亲为红,且叔叔存在并为黑那么cur节点一定不是新插入的节点,因为如果cur是新插入的节点,那么对于原来的树上图的p为红u为黑,最左边路径只有一个黑色节点,而右边至少已经有两个黑节点了,就不满足各路径黑色节点个数相同的情况了,因此cur节点一定不是新插入的节点。
如果叔叔不存在,那么cur节点一定是新插入的节点,因为如果cur节点不是新插入的节点的话那么他这条路径上之前一定会有一个黑色节点+空黑节点,而叔叔那条路径上只有空的黑节点,这样就会违反各路径黑色节点个数相同的规则,因此cur一定是新插入的节点。
对于叔叔存在且为黑和叔叔不存在的这两种情况,我们可以合并成一种,因为他们的处理方式相同。因为此时如果不想让由连续的红色节点,就一定需要有一个染黑,而只要有一个染黑就会导致各路径上的黑色节点不相同,因此对于这两种情况我们已经不再是简单的染色问题了,而是要进行旋转且保证该树仍满足搜索二叉树的性质。
对于平衡二叉搜索树(AVL树),如何旋转的方式我们是通过平衡因子来判断的,而对于红黑树而言,它的节点中不包含平衡因子的,所以我们要通过当前节点的位置来判断,分为两种该节点是在父亲的左边和右边
在这里插入图片描述
在这里插入图片描述
对于单旋来说,节点的颜色改变是:爷爷变红,父亲变黑
首先上面的cur节点位置都是位于左左,然后对于叔叔存在且为黑和叔叔不存在这两种情况,处理方式相同都是进行右旋。同理如果新插入节点是位于右右,对于叔叔存在且为黑和叔叔不存在这两种情况,处理方式相同都是进行左旋
在这里插入图片描述
在这里插入图片描述
cur节点相对于爷爷位于左右,当叔叔存在且为黑或者叔叔不存在可归为一类,都是以父亲节点为中心先进行右旋,在第一次右旋之后就将问题转化为cur节点位于爷爷的左左,且叔叔节点不存在或者叔叔节点存在且为黑的情况了,此时在对转化后的cur节点进行右旋,就可得到最终结果。
对于双旋来说,节点颜色的调整是:cur节点变黑,爷爷节点变红
注意双旋,在第一次旋转时并没有去改变任何节点的颜色,而是把旋转后的情况转化为一种需要单旋情况,然后在对这次单旋的结果进行颜色调整,仍是将爷爷节点变红,父亲节点变黑,只不过此时的此时的cur已经不是第一次旋转前的cur了,原来的cur已经变成现在cur的父亲节点了
双旋后要对颜色的调整动作一致,以左右双旋为例

//     g
//   p
//		c
RotateL(parent);
RotateR(grandfather);

cur->_col = BLACK;
grandfather->_col = RED;

同理若cur节点位于爷爷的右左,那么我们就要先对父亲节点进行右旋,旋转之后转化为cur节点位于爷爷的右右,此时再对父亲节点进行左旋就可得到最终结果。

调整总结

无论是叔叔存在且为红还是叔叔不存在或者叔叔存在且为黑这三种情况中的任何一个,我们首先需要做的就是要去判断叔叔的位置,是在爷爷节点的左边还是爷爷节点的右边,因此我们在对节点进行调整的时候,可以分为两大类,一类是叔叔在爷爷的左边,另一类是叔叔在爷爷的右边,而要怎么调整,我们又可以在每个大类里面分为两个小类,即叔叔存在且为红或叔叔不存在和叔叔存在且为黑,此时又要判断当前节点位于父亲节点的哪一侧。上述与AVL树类似,就是cur节点相对于爷爷是位于左左,右右,左右还是右左这四种情况进行调整。旋转方式与AVL基本相同,不同的是红黑树修改节点的颜色,AVL树修改节点的平衡因子
在这里插入图片描述

右旋

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

void RotateR(Node* parent)
{
	++_rotateCount;

	Node* cur = parent->_left;
	Node* curright = cur->_right;

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

	Node* ppnode = parent->_parent;
	cur->_right = parent;
	parent->_parent = cur;

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

		cur->_parent = ppnode;
	}
}

左旋

void RotateL(Node* parent)
{
	++_rotateCount;

	Node* cur = parent->_right;
	Node* curleft = cur->_left;

	parent->_right = curleft;
	if (curleft)
	{
		curleft->_parent = parent;
	}

	cur->_left = parent;

	Node* ppnode = parent->_parent;

	parent->_parent = cur;

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

		}

		cur->_parent = ppnode;
	}
}

对于修改颜色问题我们统一放在旋转之后进行修改的,这样做更方便对颜色进行修改,因为我们已经在外部定义过cur,parent,grandfather了,不必在旋转函数里进行找这几个节点了。
无论是左旋还是右旋我们发现父亲节点是一定要变黑的
例如:

RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;

旋转总结

对于AVL树,是通过平衡因子,即树的高度来判断的,左边高就要向右旋转进行平衡,相反右边高就要向左边旋转进行平衡。而对于红黑树而言,高度其实就是连续红节点,左子树右连续红节点那么就是左边高,所以我们要向右旋转,若右边有连续红节点,我们就要进行左旋。AVL树和红黑树旋转方式相同,只不过一个是通过平衡因子来判断树的高度,一个是通过连续红节点来判断高度
在这里插入图片描述

对于类似这两种情况,既然会出现,就证明各路径黑色节点数目一定是相同的,因此我们可以判定图一的a,b,c一定不为空且一定包含黑色节点,图二也是如此。
对于AVL树和红黑树又一个共同点就是在旋转时,如果是左右就要先去旋转转化为左左,对于右左就要先去旋转转化为右右

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

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

相关文章

【高效开发工具系列】MapStruct入门使用

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Node.js+Express+Nodemon+Socket.IO构建Web实时通信

陈拓 2023/11/23-2023/11/27 1. 简介 Websocket WebSocket是一种在单个TCP连接上提供全双工通讯的协议。特别适合需要持续数据交换的服务,例如在线游戏、实时交易系统等。 Websocket与Ajax之间的区别 Ajax代表异步JavaScript和XML。它被用作一组Web开发技术&…

Linux——使用命令查看文件和文件夹数量

目录 一、相关命令参数解析二、查看当前目录下的文件数量2.1 包含子目录中的文件2.2 不包含子目录中的目录 三、查看当前目录下的文件夹个数3.1 不包含目录中的目录3.2 包含目录中的目录 四、查看当前文件夹下叫某某的文件的数量实例 五、总结 一、相关命令参数解析 "&qu…

山西电力市场日前价格预测【2023-11-27】

日前价格预测 预测说明: 如上图所示,预测明日(2023-11-27)山西电力市场全天平均日前电价为40.02元/MWh。其中,最高日前电价为293.07元/MWh,预计出现在17:45。最低日前电价为0.00元/MWh,预计出现…

逆袭之战,线下门店如何在“?”萧条的情况下实现爆发增长?

未来几年,商业走势将受到全球经济形势、科技进步和消费者需求变化等多种因素的影响。随着经济复苏和消费者信心提高,消费市场将继续保持增长,品质化、个性化、智能化等将成为消费趋势。同时,线上购物将继续保持快速增长&#xff0…

Java(119):ExcelUtil工具类(org.apache.poi读取和写入Excel)

ExcelUtil工具类(XSSFWorkbook读取和写入Excel)&#xff0c;入参和出参都是&#xff1a;List<Map<String,Object>> 一、读取Excel testdata.xlsx 1、new XSSFWorkbook对象 File file new File(filePath); FileInputStream fis new FileInputStream(…

TikTok数据分析:如何通过数字洞察提升内容质量?

引言 TikTok作为全球最热门的短视频平台之一&#xff0c;每天吸引着亿万用户发布和观看各类内容。在这个充满创意的舞台上&#xff0c;内容质量成为吸引关注和提高曝光度的关键。 而要达到这一目标&#xff0c;数字数据分析成为不可或缺的工具。本文将深入探讨如何通过TikTok数…

高端制造业中的通用性超精密3D光学测量仪器

超精密光学3D测量仪器具有高精度、自动化程度高、实时反馈和范围广等优势。它能够实现微米级别的精确测量&#xff0c;能够精确测量产品的尺寸、形状和表面粗糙度等&#xff0c;具有广泛的应用价值和重要意义。 超精密光学3D测量仪器配备多种传感器、控制器和计算机系统&#…

Docker智驾开发环境搭建

文章目录 背景1. 什么是容器?2. 什么是Docker?2.1 Docker架构3. 为什么要使用Docker?3.1 Docker容器虚拟化的好处3.2 Docker在开发和运维中的优势4. Docker容器与传统虚拟化的区别4.1 区别4.2 Docker的优势5. Docker的核心概念6. Docker在嵌入式开发中的应用7. docker实践参…

企业网络中的身份安全

随着近年来数字化转型的快速发展&#xff0c;企业使用的数字身份数量急剧增长。身份不再仅仅局限于用户。它们现在扩展到设备、应用程序、机器人、第三方供应商和组织中员工以外的其他实体。即使在用户之间&#xff0c;也存在不同类型的身份&#xff0c;例如属于IT管理员、远程…

IDEA中也能用postman了?

Postman是大家最常用的API调试工具&#xff0c;那么有没有一种方法可以不用手动写入接口到Postman&#xff0c;即可进行接口调试操作&#xff1f;今天给大家推荐一款IDEA插件&#xff1a;Apipost Helper&#xff0c;写完代码就可以调试接口并一键生成接口文档&#xff01;而且还…

OpenHarmony模块化编译

一、环境配置 OpenHarmony版本&#xff1a;OpenHarmony 4.0 Release 编译环境&#xff1a;WSL2 Ubuntu 18.04 平台设备&#xff1a;RK3568 二、配置hb OpenHarmony 代码构建有build.sh和hb两种方式: #方式一、build.sh ./build.sh --product-name rk3568 --ccache#方式二、…

CSC公派博士后|管理学老师赴韩国首尔大学达成目标

J老师自身背景正好卡在CSC公派博士后申报条件的边缘&#xff0c;为增大通过概率&#xff0c;其提出优选亚洲范围内的世界知名高校、专业相符、2年博士后职位的要求。最终我们用韩国首尔大学的邀请函助其顺利获批CSC&#xff0c;实现了所有既定目标。 J老师背景&#xff1a; 申…

Linux使用固定ip地址

设置静态ip&#xff0c;我们就需要修改 /etc/sysconfig/network-scripts/ifcfg-ens33 配置文件。 vim /etc/sysconfig/network-scripts/ifcfg-ens33 //进入网卡ens33的配置页面 (1) 将 BOOTPROTO dhcp 改成 BOOTPROTO static 也就是将动态ip&#xff0c;改成静态i…

【Java Spring】SpringBoot常用插件

文章目录 1、Lombok1.1 IDEA社区版安装Lombok1.2 IDEA专业版安装Lombok1.3 Lombok的基本使用 2、EditStarters2.1 IDEA社区版安装EditStarters2.2 IDEA专业版安装EditStarters2.3 EditStarters基本使用方法 1、Lombok 是简化Java开发的一个必要工具&#xff0c;lombok的原理是…

腾讯云优惠券如何领取(腾讯云在哪领取优惠券)

腾讯云作为国内领先的云计算服务提供商&#xff0c;一直致力于为用户提供优质、高效、安全的云计算服务。为了吸引广大用户&#xff0c;腾讯云经常推出各种优惠活动&#xff0c;其中就包括优惠券的发放。那么&#xff0c;腾讯云优惠券如何领取呢&#xff1f;本文将为大家详细解…

虚幻学习笔记—点击场景3D物体的两种处理方式

一、前言 本文使用的虚幻引擎为5.3.2&#xff0c;两种方式分别为&#xff1a;点击根物体和精准点击目标物体。 二、实现 2.1、玩家控制器中勾选鼠标点击事件&#xff1a;这一步很重要&#xff0c;如图2.1.1所示&#xff1a;在自定义玩家控制器中勾 图2.1.1 选该项&#xff0c…

CentOS7.9虚拟机EDA环境,支持模拟集成电路、数字集成电路、数模混合设计全流程,包含工艺库

目录 前言一、配置准备工作1.1 网盘文件说明1.2 EDA工具介绍 二、虚拟机运行2.1 虚拟机工具启动2.2 软件配置使用2.3 Module工具切换环境变量和软件版本 获取方法附录&#xff1a;部分EDA工具运行效果图 前言 搭建了CentOS7.9虚拟机环境&#xff0c;工具包括但不限于&#xff…

静态方法和属性的经典使用-单例设计模式

单例设计模式 一、设计模式二、单例模式1、饿汉式2、懒汉式3、区别 单例设计模式是静态方法和属性的经典使用。 一、设计模式 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式就像是经典的棋谱&#xff0c;不同的棋局&…

uniapp基础-教程之HBuilderX基础常识篇02

uniapp创建项目时属性多为vue后缀&#xff1b;其中每个文件中都包含了三段式结构分别是template&#xff1b;script&#xff1b;style形势&#xff0c;分别是前端显示的画面以及js和css样式。 template&#xff1a;说大白话就是给别人看的&#xff0c;我们打开页面就可以看到的…