手撕红黑树、三种情况就可玩转红黑

news2024/10/7 16:15:30

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
在这里插入图片描述

文章目录

  • 一、红黑树概念
  • 二、红黑树性质
  • 三、红黑树 插入
    • ①变色(c红 p红 g黑 u存在且红)
    • ②旋转(c红 p红 g黑 u存在且黑/不存在)
    • ③双旋转(c红 p红 g黑 u存在且黑/不存在)
  • 四、旋转代码
  • 五、红黑树插入代码
  • 六、红黑树平衡判断

红黑树认为,AVL数控制的太严格,越严格就会有越多的旋转
AVL树严格平衡,红黑树近似平衡

这样的话红黑树也会造成一些不好的后果,比如查找某些数据次数多一些,略慢一些,但是大部分情况还是在中间就找到了,所以查找效率大差不差,对于CPU而言,没什么差别,但是红黑树能具有更少的旋转!!!

一、红黑树概念

红黑树,是一种搜索二叉树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black,通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因为是接近平衡的。
在这里插入图片描述

二、红黑树性质

1.每个结点不是黑色就是红色
2.根结点是黑色
3.不存在连续两个红色的结点
4.每条路径的黑色结点数目相同
5.每个叶子都是黑色结点(指的是空结点NIL),加上NIL结点就是方便画图数路径

这样如何保证最长路径不超过最短路径的两倍呢?
极限最短:全黑
极限最长:一黑一红

三、红黑树 插入

在这里插入图片描述
现在要新插入结点,插入什么颜色?
如果是黑色,那么这一条路径的黑色路径和其他路径都不同了
如果是红色,那还好说,只有可能是该路径上的上一个和自己不同

红黑树被影响了之后有两个方向
1.变色
2.旋转

在这里插入图片描述
如果在27右边插入一个结点,那么27一定要先变黑,不然的话,就违背不能连续两个红,而此时如果不变27,而去变新插入的颜色,那又违背每条路径黑色结点数量相等的原理。

红黑树遇事不决看叔叔

新插入结点cur
新插入结点的父亲27parent
新插入结点的叔叔22uncle
父亲的父亲grandfather

①变色(c红 p红 g黑 u存在且红)

在这里插入图片描述

具象图:
在这里插入图片描述

第一步,parent变黑,uncle一起变黑,因为右边多了一个黑,uncle来平衡左右,然后grandfather变红,一直往上走,到为止,然后根变黑即可在这里插入图片描述
也就是说,这种情况只需要没条路径都多一个黑结点就可以了



有些情况单单变色解决不了
在这里插入图片描述
比如事先就有一个uncle结点是黑色,无法向这条路径增添多的黑,此时,就需要分情况来进行旋转了,需要注意的是新增添的结点一定是红,因为为了不破坏每条路径黑色结点数量相同的原理

②旋转(c红 p红 g黑 u存在且黑/不存在)

在这里插入图片描述
Ⅰ、如果u不存在,那么cur就是新增,因为cur一定是红,此时如果不是新增,就违背了不能两个连续红,不存在的时候旋转也不碍事
Ⅱ、uncle存在且为黑,这时cur一定为黑,那么现在cur为红的原因是子树因为颜色的调整,把cur变红

这时,uncle是黑色,父亲无法变色,不然的话就这条路径多了一个黑,所以这种情况就需要旋转:把grandfather旋转下来变成红色,parent变黑
在这里插入图片描述
子树中还有其他黑色结点,不用担心

③双旋转(c红 p红 g黑 u存在且黑/不存在)

同理,uncle不存在,cur即为新增,就算不存在,我旋转以下也不碍事
在这里插入图片描述

此时如果对p右单旋,再进行刚刚的变色操作的话,就会让右边多两个黑色结点,因为黑色结点在cur的字数中,而现在因为单旋让cur到了右树

所以这里的正确操作时需要双旋,首先将cur旋转到左树上去,也就是对p左单旋,转变为情况二进行单旋
在这里插入图片描述

四、旋转代码

//左旋
void RotateL(Node*parent)
{
	Node*subR = parent->_right;
	Node*subRL = subR->_left;
	parent->_right = subRL;
	if(subRL)
	{
		subRL->_parent = parent;
	}
	Node*ppNode=parent->_parent;
	subR->_left = parent;
	parent->_parent = subR;
	if(_root==parent)
	{
		_root=subR;
		subR->_parent=nullptr;
	}
	else
	{
		if(ppNode->_left == parent)
			ppNode->_left = subR;
		else
			ppNode->_right = subR;
		subR->_parent = ppNode;
	}
}
//右旋
void RotateR(Node*parent)
{
	Node*subL = parent->_left;
	Node*subLR = subL->_right;
	parent->_left = subLR;
	if(subLR)
	{
		subLR->_parent = parent;
	}
	Node*ppNode=parent->_parent;
	subL->_right = parent;
	parent->_parent = subL;
	if(_root==parent)
	{
		_root=subL;
		subL->_parent=nullptr;
	}
	else
	{
		if(ppNode->_left == parent)
			ppNode->_left = subL;
		else
			ppNode->_right = subL;
		subL->_parent = ppNode;
	}
}

五、红黑树插入代码

bool Insert(const pair<K,V>&kv)
{
	if(_root==nullptr)
	{
		_root=new Node(kv);
		_root->col =BLACK;//enum,根结点为黑
		return true;
	}//树为空
	Node*parent =nullptr;
	Node*cur = _root;
	while(cur)//找到插入位置
	{
		if(cur->kv.first<kv.first)
		{
			parent=cur;
			cur=cur->_right;
		}
		else if(cur->_kv.first>kv.first)
		{
			parent = cur;
			cur=cur->_left;
		}
		else//有相同键值
		{
			return false;
		}
	}
	cur = new Node(kv);//传键值对new
	cur->_col=RED;//新插入一定是red
	//看插入哪边
	if(parent->_kv.first<kv,first)
	{
		parent->_right = cur;
	}
	else
	{
		parent->_left = cur;
	}
	cur -> _parent = parent;
	
	//红黑树的三种情况的处理
	while(parent&&parent->_col==RED)//需要继续
	{
		Node*grandfather = parent->_parent;
		assert(grandfather&&grandfather0>_col==BLACK);
		//不为nullptr且不能和parent一起红
		Node*uncle;
		if(parent==grandfather->left)
		{
			uncle == grandfather->_right;
			if(uncle&&uncle->_col==RED)
			{//情况一,变色即可
				parent->_col=uncle->_col = BLACK;
				grandfather->_col = RED;
				//继续往上
				cur = grandfather;
				parent=cur->_parent;
			}
			else//情况二三
			{
				if(cur == parent->_left)
				{
					RotateR(grandfather);//右旋
					grandfather->_col=RED;
					parent->_col= BLACK;
				}
				else if(cur==parent->_right)
				{
					RotateL(parent);
					RotateR(grandfather);
					cur->_col = BLACK;
					grandfather ->_col= RED;
				}
			}
		}
		else if(parent==grandfather->_right)
		{
		//相同代码,只是换了一边
			uncle = grandfather->_left;
			if(uncle&&uncle->_col==RED)
			{
				parent->_col = uncle->_col=BLACK;
				grandfather->_col = RED;
				cur=grandfather;
				parent=cur->_parent;
			}
			else
			{
				if(cur==parent->_right)
				{
					RotateL(grandfather);
					grandfather->_col = RED;
					parent->_col = BLACK;
				}
				else if(cur == parent->_left)
				{
					RotateR(parent);
					RotateL(grandfather);
					cur->_col=BLACK;
					grandfather->_col = RED;
				}
			}
		}
	}
	_root->_col = BLACK;
	return true;
}

六、红黑树平衡判断

红黑树最长路径不超过最短路径的二倍是颜色互斥得来的,所以判断颜色是否符合要求一定靠谱。

检查
1.每条简单路径的黑色结点数量相同
2.不能连续的红色结点
3.根结点是黑色

bool IsBalance()
{
	if(_root==nullptr)
		return true;
	if(_root->_col==RED)
	{
		cout<<"根结点不是黑色"<<endl;
		return false;
	}
	int benchMark=0;
	//黑色结点数量基准值,随便选一条
	Node*cur =_root;
	while(cur)
	{
		if(cur>_col == BLACK)
			++benchMark;
		cur = cur->_left;
	}
	return PrevCheck(_root,0,benchMark);
}

bool PrevCheck(Node*root,int blackNum,int benchMark)
{
	if(root==nullptr)
	{
		if(blackNum!=benchMark)
		{
			cout<<"某条黑色结点数量不相等"<<endl;
			return false;
		}
		else
		{
			return true;
		}
	}
	if(root->_col==BLACK)
	{
		++blackNum;
	}
	if(root->_col==RED&&root->_parent->_col==RED)
	{	
		cout<<"存在连续红色结点"<<endl;
		return false;
	}
	return PrevCheck(root->_left,blackNum,benchMark)
	&&PrevCheck(root->_right,blackNum,benchMark);
}

在这里插入图片描述

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

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

相关文章

熟人服务器被黑,五种实战方法强化linux服务器安全性!

公司护网行动,五种实战方法,下面直接上实操: 1.修改ssh端口为59527,并开放防火墙端口 修改ssh配置文件 /etc/ssh/sshd_config,将端口号修改为59527.同时保留ssh默认的22端口,为了防止修改端口号失败以后,远程登录不上服务器 2.修改firewall配置 默认情况下,防火墙是…

JVM——垃圾回收

垃圾回收 1、如何判断对象可以回收? 一、引用计数法 当一个对象被其他变量引用时&#xff0c;使其计数1&#xff08;若被引用两次&#xff0c;计数为2&#xff09;&#xff0c;若某个变量不在引用它时&#xff0c;使其计数-1&#xff1b;当这个对象引用计数变为0时意味着不…

吴恩达【神经网络和深度学习】Week1——深度学习概述

文章目录1、What is a neural network?2、Supervised Learning with Neural Networks2.1、Examples2.2、The classification of data3、Why is Deep Learning taking off&#xff1f;4、Quiz课程笔记整理按照所讲章节的标题来完成1、What is a neural network? 以房价预测模型…

基于HTML5 技术的开放自动化HMI

人机交互接口&#xff08;HMI&#xff09;是自动化系统中不可或缺的一部分。传统的做法是提供一个HMI 显示屏&#xff0c;并且通过组态软件来配置显示屏的功能&#xff0c;通过modbus 或者以太网与PLC 连接。 现在&#xff0c;事情变得复杂了许多&#xff0c;用户不仅需要通过专…

干货 | 关于PCB中的“平衡铜”,一文全部说明白

平衡铜是PCB设计的一个重要环节&#xff0c;对PCB上闲置的空间用铜箔进行填充&#xff0c;一般将其设置为地平面。 平衡铜的意义在于&#xff1a; 对信号来说&#xff0c;提供更好的返回路径&#xff0c;提高抗干扰能力&#xff1b;对电源来说&#xff0c;降低阻抗&#xff0c;…

Android 13 源码获取与构建

文章目录1. 环境准备1.1 基本信息1.2 系统初始化1.2.1 更新 Ubuntu 软件包1.2.2 安装 git 工具1.2.3 安装依赖包(Ubuntu 18.04)1.2.4 修改默认python版本1.2.5 安装 repo 工具2. 源码下载完成2.1 创建源码目录2.2 初始化源码仓库2.3 开始下载源码2.4 Android 13 源码目录3. 构建…

m可见光通信的空间调制(sm)误码率matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 可见光通信技术&#xff08;Visible Light Communication&#xff0c;VLC&#xff09;是指利用可见光波段的光作为信息载体&#xff0c;在空气中直接传输光信号的通信方式。可见光通信技术绿色低…

virtio vring原理

vring原理 在 virtio 设备上进行批量数据传输的机制被称为 virtqueue 。每个设备可以拥有零个或多个 virtqueue &#xff0c;当 Driver 想要向设备发送数据时&#xff0c;它会填充 Descriptor Table 中的一项&#xff08;或将几项链接在一起&#xff09;&#xff0c;并将描述符…

圣诞树拼图游戏unity制作

2022年圣诞节到来啦&#xff0c;很高兴这次我们又能一起度过~ 一、前言 提示&#xff1a;使用unity来制作一个拼图游戏&#xff0c;图片便是圣诞树。 二、创意名 圣诞树拼图游戏 三、效果展示 圣诞树拼图游戏最终效果。 游戏中效果如图&#xff1a; 游戏拼图完成后效果如图&am…

vue实现随机生成分享海报(内容动态)

大家好&#xff0c;我是雄雄。 前言 昨天写了篇文章&#xff1a;自己整理的vue实现生成分享海报&#xff08;含二维码&#xff09;&#xff0c;看着网上的没实现 主要是介绍了如何使用vue实现&#xff0c;动态分享内容为海报&#xff0c;且附带二维码&#xff0c;扫描二维码能…

shell脚本四剑客之awk详解

文章目录awk的介绍awk能够干什么awk的格式工作原理&#xff1a;记录和域内建变量的用法1. FS2. OFS3.RS4. ORS5. NF6. NRBEGIN 和END语句块常见案例1. 使用NR行号提取ip2. 打印UID小于10的账号名称和UID信息3. 数学运算4. AWK打印硬盘设备名称&#xff0c;默认以空格为分割&…

UDP用户数据报协议(计算机网络-运输层)

目录 UDP 概述 UDP 的主要特点 UDP 的问题 UDP的多路分用模型 UDP 的首部格式 UDP 概述 用户数据报协议&#xff08;User Datagram Protocol&#xff0c;UDP&#xff09; UDP 只在 IP 的数据报服务之上增加了很少一点的功能&#xff0c;即端口的功能和差错检测的功能 虽…

计算机网络——网络层功能概述

网络层 网络层的主要任务是把分组从源端传到目的端&#xff0c;为分组交换网上的不同主机提供通信服务。网络层的传输单位成为数据报。 数据报是一组比较长的数据&#xff0c;分组则是将数据报划分为不同的片段 网络层的第一个功能&#xff1a;路由的选择和分组的转发。 网络层…

python词云图词频统计

目录 一&#xff1a;安装必要的库 二&#xff1a;数据分析 条形图可视化 三&#xff1a;数据分析 词频统计 词云图可视化 一&#xff1a;安装必要的库 导入必要的库 import collections # 词频统计库 import os import re # 正则表达式库 import urllib.error # 指定url&…

WRF进阶:antro_emiss工具处理全球大气人为排放(EDGRA_HTTPs)/人为排放清单前处理

本内容视频版讲解&#xff1a;全球人为排放处理 介绍 一般人为数据的排放前处理使用pre_chen_src工具&#xff0c;然而pre_chen_src处理后的文件并不是WRF所能读取的文件格式&#xff0c;需要使用onvert_emiss.exe&#xff0c;生成WRF需要的人为排放的nc数据。 在WRF-chem3.6…

煤矿视频监控分析检测 yolo

煤矿视频监控分析检测利用python基于yolo深度学习架构&#xff0c;对现场画面进行实时分析检测。我们使用YOLO(你只看一次)算法进行对象检测。YOLO是一个聪明的卷积神经网络(CNN)&#xff0c;用于实时进行目标检测。该算法将单个神经网络应用于完整的图像&#xff0c;然后将图像…

单片机——LED点阵

1. 基本介绍 LED点阵 LED点阵是由发光二极管排列组成的显示器件&#xff0c;通常应用较多的是88点阵&#xff0c;然后通过多个88点阵组成不同分辨率的LED点阵显示屏&#xff0c;如4个88组成的1616点阵 8*8点阵由64个LED组成&#xff0c;每个LED是放置在行线和列线的交叉点上…

LVGL学习笔记3 - 样式Style

目录 1. 初始化样式 2. 设置样式 3. 添加和移除样式 4. 验证 5. 状态&#xff08;State&#xff09; 6. 部分&#xff08;Parts&#xff09; 样式用于设置对象的外观&#xff0c;比如颜色等属性&#xff0c;存储在 lv_style_t 变量中&#xff0c;这个变量应该是static…

不写一行代码(二):实现安卓基于PWM的LED设备驱动

文章目录一、前言二、系列文章三、准备工作3.1 查找PWM引脚3.2 原理图&#xff1a;确认引脚位置3.3 PWM Controller四、查阅PWM bindings五、编写设备树节点5.1 实现节点&#xff1a;pwm-leds5.2 测试命令六、后语一、前言 在完成了基于GPIO的LED设备驱动的文章后&#xff0c;…

3天学会撰写软件发明专利——3.生命周期

“无意中发现了一个巨牛的人工智能教程&#xff0c;忍不住分享一下给大家。教程不仅是零基础&#xff0c;通俗易懂&#xff0c;而且非常风趣幽默&#xff0c;像看小说一样&#xff01;觉得太牛了&#xff0c;所以分享给大家。点这里可以跳转到教程。”。 一、专利授权生命周期…