【数据结构】红黑树

news2025/8/2 7:48:35

红黑树

  • 一、红黑树的概念
  • 二、红黑树的接口
    • 2.1 插入
  • 三、验证
  • 四、源码

一、红黑树的概念

在这里插入图片描述
红黑树也是一个二叉搜索树,他是通过对任何一条从根到叶子的路径上各个结点着色方式的限制,最长路径长度不超过最短路径长度的 2 倍保持近似平衡。他在每个节点添加了一个变量用来表示颜色 :Black或者Red,为了满足上面的条件,着色必须满足性质:

1️⃣每个结点不是红色就是黑色
2️⃣ 根节点是黑色的
3️⃣ 如果一个节点是红色的,则它的两个孩子结点是黑色的(没有连续的红色节点)
4️⃣ 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
5️⃣ 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

由此可以满足最长路径长度不超过最短路径长度的 2 倍(通过第四点就可以看出)

既然不能保证绝对平衡,那么搜索性能肯定不如AVL树,那么为什么还要有红黑树呢?

首先要知道AVL树保持平衡的方法是频繁的旋转,而红黑树则不需要严格的平衡,会少很多旋转

二、红黑树的接口

红黑树节点定义:
节点需要有个颜色的变量,可以使用枚举的方法:

enum Colour
{
	RED,
	BLACK,
};

template <class K, class V>
struct RBTreeNode
{
	RBTreeNode(const pair<K, V>& kv)
		: _kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(RED)
	{}

	pair<K, V> _kv;
	AVLNode<K, V>* _left;
	AVLNode<K, V>* _right;
	AVLNode<K, V>* _parent;
	Colour _col;
};

2.1 插入

我们可以看到节点初始化的时候默认为RED,因为如果要插入BLACK,那么一定会导致错误,不满足对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
所以只能把新节点默认设置为RED,因为如果是红色有可能父节点是黑色,这样就没有出现连续的红色。
总结一下:

插入黑色节点一定有问题,插入红色节点有可能会出问题。

插入的流程根AVL树一样,检查父亲节点,如果是黑就结束,如果是红就要调整红黑树

为了方便说明,cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点
首先要知道最主要的是看u
情况一:cur为红,p为红,g为黑,u存在且为红 :
在这里插入图片描述
为什么要看u节点,因为如果cur为红且p为红,那么g一定为黑。所以唯一的变数就为u

它的调整方法为:
在这里插入图片描述
首先p肯定要变黑,而为了使g两边的子树黑节点数目相同,u也要变黑。至于g,我们先把它变红,因为如果这颗树是子树而g还是黑,那么相当于这颗子树的黑节点多了一个,会影响到别的子树。如果g为根那么就把g变为黑。
这里要注意继续往上处理:
把g当成cur,继续向上调整。

举个例子:
在这里插入图片描述
可以看到绿色部分就为上面的抽象图,就这么往上循环改变颜色即可。

情况二:cur为红,p为红,g为黑,u不存在/u为黑
在这里插入图片描述
此时要对u分情况讨论:

1️⃣ u不存在时,那么cur一定是新增节点,因为如果cur不是新增节点,那么cur和p一定有一个节点为黑,这样就不满足黑节点数目相同的条件。

在这里插入图片描述
处理方式就为右单旋
在这里插入图片描述

2️⃣ u存在且为黑
在这里插入图片描述
总结一下:

u不存在则cur是新增节点,u存在那么就是由情况一变换过来的。
情况二的处理方法就是旋转+变色

情况三: cur为红,p为红,g为黑,u不存在/u为黑

情况三与情况二的区别就是情况二是直线,情况三是折线,经过AVL的学习我们知道这种情况要双旋

在这里插入图片描述
情况三也是由其他情况变过来的。
此时我们就需要进行双旋调整红黑树。
在这里插入图片描述
左单旋后变成了情况二,那么按照情况二的方法进行右旋即可。
在这里插入图片描述
以上三种情况的代码如下:

while (parent && parent->_col == RED)
{
	// 找g 与 u
	Node* g = parent->_parent;
	if (parent == g->_left)
	{
		Node* u = g->_right;
		// 情况一 u存在且为红
		if (u && u->_col == RED)
		{
			parent->_col = u->_col = BLACK;
			g->_col = RED;
			// 继续往上处理
			cur = g;
			parent = cur->_parent;
		}
		else // 情况二或情况三
		{
			if (cur == parent->_left)// 情况二
			{
				//   g
				//  p
				// c
				RotateR(g);
				parent->_col = BLACK;
				g->_col = RED;
			}
			else// 情况三
			{
				//  g
				// p
				//  c
				RotateL(parent);
				RotateR(g);
				//   c
				// p   g
				cur->_col = BLACK;
				g->_col = RED;
			}
			break;
		}
	}
	else
	{
		Node* u = g->_left;
		// 情况一
		if (u && u->_col == RED)
		{
			u->_col = parent->_col = BLACK;
			g->_col = RED;
			cur = g;
			parent = cur->_parent;
		}
		else
		{
			// 情况二
			// g
			//  p
			//   c
			if (cur == parent->_right)
			{
				RotateL(g);
				parent->_col = BLACK;
				g->_col = RED;
			}
			else// 情况三
			{
				// g
				//  p
				// c
				RotateR(parent);
				RotateL(g);
				cur->_col = BLACK;
				g->_col = RED;
			}
			break;
		}
	}
}
// 上面有可能把_root的颜色变为红
_root->_col = BLACK;
return true;
}

三、验证

想要验证是否是红黑树,首先要保证是搜索树(中序遍历有序)。
其次还要判断根节点是否为黑,是否有两个红的相连(检查红节点的父亲),每条路径上的黑节点数目相同(随便找一条路径测出标准值)。

怎么测每条路径的黑节点数目是否相同?

测一条路径的黑节点数目当作标准值,递归过程中遇到黑节点就记录,到空说明该路径走完,比对标准值,如果不同就返回false。

bool _IsBalance(Node* root, int i, int flag)
{
	if (root == nullptr)
	{
		if (i != flag)
		{
			cout << "errno: 左右子树黑色节点数目不同" << endl;
			return false;
		}
		return true;
	}
	// 红节点时判断父亲
	if (root->_col == RED)
	{
		if (root->_parent->_col == RED)
		{
			cout << "errno: 红-红" << endl;
			return false;
		}
	}
	if (root->_col == BLACK)
	{
		i++;
	}

	return _IsBalance(root->_left, i, flag) 
		&& _IsBalance(root->_right, i, flag);
}

bool IsBalance()
{
	if (_root == nullptr)
	{
		return true;
	}
	if (_root->_col != BLACK)
	{
		return false;
	}
	// 找标准值
	Node* cur = _root;
	int flag = 0;
	while (cur)
	{
		if (cur->_col == BLACK)
		{
			flag++;
		}
		cur = cur->_left;
	}
	int i = 0;
	return _IsBalance(_root, i, flag);
}

四、源码

#pragma once
#include <iostream>
#include <cstdlib>
#include <cassert>
#include <string>

using namespace std;

enum Colour
{
	RED,
	BLACK,
};

template <class K, class V>
struct RBTreeNode
{
	RBTreeNode(const pair<K, V>& kv)
		: _kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(RED)
	{}

	pair<K, V> _kv;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	Colour _col;
};

template <class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
	bool insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			return true;
		}

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

		while (parent && parent->_col == RED)
		{
			// 找g 与 u
			Node* g = parent->_parent;
			if (parent == g->_left)
			{
				Node* u = g->_right;
				// 情况一 u存在且为红
				if (u && u->_col == RED)
				{
					parent->_col = u->_col = BLACK;
					g->_col = RED;
					// 继续往上处理
					cur = g;
					parent = cur->_parent;
				}
				else // 情况二或情况三
				{
					if (cur == parent->_left)// 情况二
					{
						//   g
						//  p
						// c
						RotateR(g);
						parent->_col = BLACK;
						g->_col = RED;
					}
					else// 情况三
					{
						//  g
						// p
						//  c
						RotateL(parent);
						RotateR(g);
						//   c
						// p   g
						cur->_col = BLACK;
						g->_col = RED;
					}
					break;
				}
			}
			else
			{
				Node* u = g->_left;
				// 情况一
				if (u && u->_col == RED)
				{
					u->_col = parent->_col = BLACK;
					g->_col = RED;
					cur = g;
					parent = cur->_parent;
				}
				else
				{
					// 情况二
					// g
					//  p
					//   c
					if (cur == parent->_right)
					{
						RotateL(g);
						parent->_col = BLACK;
						g->_col = RED;
					}
					else// 情况三
					{
						// g
						//  p
						// c
						RotateR(parent);
						RotateL(g);
						cur->_col = BLACK;
						g->_col = RED;
					}
					break;
				}
			}
		}
		// 上面有可能把_root的颜色变为红
		_root->_col = BLACK;
		return true;
	}

	void RotateL(Node* parent)
	{
		Node* top = parent->_parent;
		Node* right = parent->_right;
		parent->_right = right->_left;
		if (right->_left) right->_left->_parent = parent;
		right->_left = parent;
		parent->_parent = right;
		if (top)// 子树
		{
			if (parent == top->_left) top->_left = right;
			else top->_right = right;
			right->_parent = top;
		}
		else// 完整的树
		{
			_root = right;
			_root->_parent = nullptr;
		}
	}

	void RotateR(Node* parent)
	{
		Node* top = parent->_parent;
		Node* left = parent->_left;
		Node* leftR = left->_right;
		parent->_left = leftR;
		if (leftR) leftR->_parent = parent;
		left->_right = parent;
		parent->_parent = left;
		if (top)
		{
			if (parent == top->_left) top->_left = left;
			else top->_right = left;
			left->_parent = top;
		}
		else
		{
			_root = left;
			_root->_parent = nullptr;
		}
	}

	void _Inorder(Node* root)
	{
		if (root == nullptr)
			return;

		_Inorder(root->_left);
		cout << root->_kv.first << "<=>" << root->_kv.second << endl;
		_Inorder(root->_right);
	}

	void Inorder()
	{
		_Inorder(_root);
	}

	bool _IsBalance(Node* root, int i, int flag)
	{
		if (root == nullptr)
		{
			if (i != flag)
			{
				cout << "errno: 左右子树黑色节点数目不同" << endl;
				return false;
			}
			return true;
		}
		// 红节点时判断父亲
		if (root->_col == RED)
		{
			if (root->_parent->_col == RED)
			{
				cout << "errno: 红-红" << endl;
				return false;
			}
		}
		if (root->_col == BLACK)
		{
			i++;
		}

		return _IsBalance(root->_left, i, flag) 
			&& _IsBalance(root->_right, i, flag);
	}

	bool IsBalance()
	{
		if (_root == nullptr)
		{
			return true;
		}
		if (_root->_col != BLACK)
		{
			return false;
		}
		// 找标准值
		Node* cur = _root;
		int flag = 0;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				flag++;
			}
			cur = cur->_left;
		}
		int i = 0;
		return _IsBalance(_root, i, flag);
	}

private:
	Node* _root = nullptr;
};

void test()
{
	RBTree<int, int> bb;
	const int N = 10000;
	srand(time(0));
	for (int i = 0; i < N; i++)
	{
		size_t x = rand();
		bb.insert(make_pair(x, x));
	}
	/*int a[] = { 16, 3, 7, 11, 9, 26, 18, 14};
	for (auto e : a)
	{
		bb.insert(make_pair(e, e));
	}*/
	cout << bb.IsBalance();
}

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

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

相关文章

华为OD机试题,用 Java 解【勾股数元组】问题

最近更新的博客 华为OD机试 - 猴子爬山 | 机试题算法思路 【2023】华为OD机试 - 分糖果(Java) | 机试题算法思路 【2023】华为OD机试 - 非严格递增连续数字序列 | 机试题算法思路 【2023】华为OD机试 - 消消乐游戏(Java) | 机试题算法思路 【2023】华为OD机试 - 组成最大数…

骨传导耳机靠谱吗,骨传导耳机的原理是什么

很多人刚开始接触骨传导耳机时都会具有一个疑问&#xff0c;骨传导耳机是不是真的靠谱&#xff0c;是不是真的不伤害听力&#xff1f;骨传导耳机传输声音的原理是什么&#xff1f; 下面就给大家讲解一下骨传导耳机传输声音的原理以及骨传导耳机对听力到底有没有伤害。 骨传导…

Python编写GUI界面,实现小说下载器

嗨害大家好鸭&#xff01;我是小熊猫~思路一、数据来源分析二. 代码实现步骤代码实现一、单章小说下载二、整本小说下载三、多线程采集四、采集排行榜所有小说五、搜索小说功能六、GUI界面<center>**&#x1f447;问题解答 源码获取 技术交流 抱团学习请联系&#x1f…

【蓝桥集训】第七天——并查集

作者&#xff1a;指针不指南吗 专栏&#xff1a;Acwing 蓝桥集训每日一题 &#x1f43e;或许会很慢&#xff0c;但是不可以停下来&#x1f43e; 文章目录1.亲戚2.合并集合3.连通块中点的数量有关并查集的知识学习可以移步至—— 【算法】——并查集1.亲戚 或许你并不知道&#…

华为OD机试题,用 Java 解【喊 7 的次数重排】问题

最近更新的博客 华为OD机试 - 猴子爬山 | 机试题算法思路 【2023】华为OD机试 - 分糖果(Java) | 机试题算法思路 【2023】华为OD机试 - 非严格递增连续数字序列 | 机试题算法思路 【2023】华为OD机试 - 消消乐游戏(Java) | 机试题算法思路 【2023】华为OD机试 - 组成最大数…

大数据开发 - Java入门2

目录Java基础知识注释关键字常量标识符测试题回顾Java基础知识 注释 对程序的解释说明 分类&#xff1a; 单行注释&#xff1a;// 对本行后面的内容进行注释多行注释&#xff1a;/*解释内容 */文档注释 &#xff1a;/** 注释内容*/ --用于产生帮助文档&#xff0c;也有多行注…

高通平台开发系列讲解(SIM卡篇)SIM卡基础概念

文章目录 一、SIM卡基本定义二、卡的类型三、SIM卡的作用三、SIM卡基本硬件结构四、SIM卡的内部物理单元五、卡文件系统沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇文章将介绍SIM的相关组件。 一、SIM卡基本定义 SIM卡是一种智能卡(ICC Card/UICC Card) SIM…

在线客服系统接入网站会员,绑定会员ID,展示会员昵称头像,传递手机号等扩展字段【唯一客服】...

在客服系统聊天链接里&#xff0c;可以带上自己网站的会员信息&#xff0c;例如&#xff1a;昵称、头像、手机号等 具体使用方式如下 聊天链接中增加以下参数&#xff1a; visitor_id: 自有会员visitor_name: 自有会员名称avator: 自有会员头像lang: 多语言 cn 中文 &#xff0…

链接服务器查询导致的阻塞

背景客户反馈数据库在上午10点时出现严重阻塞&#xff0c;阻塞源头会话在等待OLEDB&#xff0c;没有见过这个等待类型&#xff0c;请我们协助分析。现象登录SQL专家云&#xff0c;进入趋势分析&#xff0c;下钻到10点钟的活动会话&#xff0c;看到发生了两次严重的阻塞。转到活…

指针的进阶【上篇】

文章目录&#x1f4c0;1.字符指针&#x1f4c0;2.指针数组&#x1f4c0;3.数组指针&#x1f4bf;3.1.数组指针的定义&#x1f4bf;3.2. &数组名VS数组名&#x1f4bf;3.3.数组指针的使用&#x1f4c0;1.字符指针 int main() {char ch w;char* pc &ch;// pc就是字符指…

数据结构之顺序表篇

一、顺序表概念 二、顺序表各类接口实现 *顺序表初始化 **顺序表销毁 ***顺序表插入操作 ****顺序表删除操作 *****顺序表查找操作 ******顺序表实现打印操作 三、顺序表整体实现源码 *SeqList.h **SeqList.c ***test.c 一、顺序表概念 讲顺序表之前先引入线性表概念&#xff…

可视化服务编排在金融APP中的实践

本文重点介绍了京东金融APP在BFF层实践过程中遇到的问题&#xff0c;并引出可视化服务编排在金融APP中的落地实践&#xff0c;其中重点介绍了可视化服务编排系统的核心功能及实现。 可视化服务编排系统已经稳定支持了金融APP从去年618到现在的所有发版迭代&#xff0c;对人效提…

Apache ActiveMQ安装和使用

文章目录Apache ActiveMQ安装和使用 环境下载安装配置启动登录Apache ActiveMQ安装和使用 环境 Ubuntu20.04 下载 官网&#xff1a;https://activemq.apache.org/download-archives 如下载5.14.4版本&#xff0c;apache-activemq-5.14.4-bin.tar.gz&#xff0c;测试过没问题…

分布式算法 - 一致性Hash算法

一致性Hash算法是个经典算法&#xff0c;Hash环的引入是为解决单调性(Monotonicity) 的问题&#xff1b;虚拟节点的引入是为了解决 平衡性(Balance) 问题。一致性Hash算法引入在分布式集群中&#xff0c;对机器的添加删除&#xff0c;或者机器故障后自动脱离集群这些操作是分布…

MySQL索引类型及原理?一文读懂

一、什么是MySQL索引&#xff1f; MySQL索引是一种数据结构&#xff0c;用于提高数据库查询的性能。它类似于一本书的目录&#xff0c;通过在表中存储指向数据行的引用&#xff0c;使得查询数据的速度更快。 在MySQL中&#xff0c;索引通常是在表上定义的&#xff0c;它们可以…

本地新创建的项目,关联/上传到码云

以下示例以mac为例&#xff0c;window将相关步骤改为windows对应的操作即可1、打开终端&#xff0c;通过终端命令 cd 切换到新建的本地项目目录下&#xff0c;如&#xff1a;&#xff08;/Users/wangcongming/Documents/TuoTuo/mobile_acitvity 为我的新建项目地址&#xff09;…

使用 ChatGPT ,通过自然语言编写 eBPF 程序和追踪 Linux 系统

eBPF 是一项革命性的技术&#xff0c;起源于 Linux 内核&#xff0c;可以在操作系统的内核中运行沙盒程序。它被用来安全和有效地扩展内核的功能&#xff0c;而不需要改变内核的源代码或加载内核模块。今天&#xff0c;eBPF被广泛用于各类场景&#xff1a;在现代数据中心和云原…

「RISC-V Arch」SBI 规范解读

术语 SBI&#xff0c;Supervisor Binary Interface&#xff0c;管理二进制接口 U-Mode&#xff0c;User mode&#xff0c;用户模式 S-Mode&#xff0c;Supervisor mode&#xff0c;监督模式 VS-Mode&#xff0c;Virtualization Supervisor mode&#xff0c;虚拟机监督模式 …

嵌入式 Linux Shell编程

目录 1、shell脚本 2、执行shell脚本 3、shell脚本编写 3.1 shell变量 3.2 标准变量或环境变量 3.4 变量赋值有五种格式 3.5 运算符和表达式 关系运算符 布尔运算符 3.6 Test命令用法 1、判断表达式 2、判断字符串 3.判断整数 4、判断文件 3.7 数组 1、数组定义…

什么是 RPA ?What is robotic process automation (RPA)?

目录 前言 What is a business process? 什么是业务流程? What does "robotic process automation" mean?“机器人过程自动化”是什么意思? What is robotic process automation (RPA)?什么是机器人流程自动化 (RPA)? What