红黑树的概念和简单实现

目录

    • 红黑树的概念
    • 红黑树的结构
    • 红黑树的插入
    • 红黑树的验证

红黑树的概念

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

在这里插入图片描述
在这里插入图片描述
红黑树的性质

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

红黑树的结构

enum Colour
{
	RED,
	BLACK
};

template<class k,class v>
struct RBTreeNode
{
public:
	RBTreeNode(const pair<k,v>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_cor(RED)
		,_kv(kv)
	{}

	RBTreeNode* _left;
	RBTreeNode* _right;
	RBTreeNode* _parent;
	pair<k, v> _kv;
	Colour _cor;
};

template<class k, class v>
class RBTree
{
	typedef RBTreeNode<k, v> Node;
public:
	bool Insert(const pair<k, v>& kv);
private:
	Node* _root = nullptr;
};

红黑树的插入

AVL树的学习
在这里插入图片描述
最简单的情况:cur为红,p为红,g为黑,u存在且为红
这种情况不需要考虑cur是在p的左还是右,只需要简单的变色,然后继续向上调整

然后要考虑的是u不存在或u为黑时的情况
这时候就需要考虑cur在p的左还是右
在这里插入图片描述

bool Insert(const pair<k, v>& kv)
{
	if (_root == nullptr)
	{
		Node* newnode = new Node(kv);
		_root = newnode;
		_root->_cor = BLACK;
		return true;
	}

	Node* cur = _root;
	Node* parent = nullptr;
	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;
		}
	}

	Node* newnode = new Node(kv);
	newnode->_cor = RED;
	if (parent->_kv.first > newnode->_kv.first)
	{
		parent->_left = newnode;
		newnode->_parent = parent;
	}
	else if (parent->_kv.first < newnode->_kv.first)
	{
		parent->_right = newnode;
		newnode->_parent = parent;
	}
	else
	{
		return false;
	}

	cur = newnode;
	parent = cur->_parent;
	//插入节点的父亲是黑色,直接结束插入

	//cur是红色的
	//parent是红色则调整
	//grand肯定存在且是黑色
	while (parent && parent->_cor == RED)
	{
		Node* grandparent = parent->_parent;
		if (parent == grandparent->_left)
		{
			//    g
			//  p   u(?)
			//c
			Node* uncle = grandparent->_right;
			//uncle存在,且为红色,c的位置不用在意
			//    g
			//  p   u
			//c
			if (uncle && uncle->_cor == RED)
			{
				//p u改成黑色,g改成红色,p变为cur,g变为parent,继续向上检查
				parent->_cor = uncle->_cor = BLACK;
				grandparent->_cor = RED;

				cur = grandparent;
				parent = grandparent->_parent;
			}
			//如果uncle不存在或者为黑色,c的位置需要注意
			//     g
			//   p   u
			//c1  c2
			else
			{
				if (cur == parent->_left)
				{
					//左左,对g右单旋,p变为新的子树的头
					RotateR(grandparent);
					//变色,p变黑,g变红
					parent->_cor = BLACK;
					grandparent->_cor = RED;
				}
				else if (cur == parent->_right)
				{
					//左右,先对p左单旋,再对g右单旋
					RotateL(parent);
					RotateR(grandparent);
					//变色,cur变为新的头,黑色,g变红
					cur->_cor = BLACK;
					grandparent->_cor = RED;
				}
				else
				{
					assert(false);
				}
				//新的头已经是黑色,没必要再向上检查
				break;
			}
		}
		else if (parent == grandparent->_right)
		{
			//     g
			//  u?   p
			//         c
			Node* uncle = grandparent->_left;
			//uncle存在,且为红色,c的位置不用在意
			//    g
			//  u   p
			//        c
			if (uncle && uncle->_cor == RED)
			{
				//p u改成黑色,g改成红色,p变为cur,g变为parent,继续向上检查
				parent->_cor = uncle->_cor = BLACK;
				grandparent->_cor = RED;

				cur = grandparent;
				parent = grandparent->_parent;
			}
			//如果uncle不存在或者为黑色,c的位置需要注意
			//    g
			//  u   p
			//    c1  c2
			else
			{
				if (cur == parent->_right)
				{
					//右右,对g左单旋,p变为新的子树的头
					RotateL(grandparent);
					//变色,p变黑,g变红
					parent->_cor = BLACK;
					grandparent->_cor = RED;
				}
				else if (cur == parent->_left)
				{
					//右左,先对p右单旋,再对g左单旋
					RotateR(parent);
					RotateL(grandparent);
					//变色,cur变为新的头,黑色,g变红
					cur->_cor = BLACK;
					grandparent->_cor = RED;
				}
				else
				{
					assert(false);
				}
				//新的头已经是黑色,没必要再向上检查
				break;
			}
		}
		else
		{
			assert(false);
		}
	}
	_root->_cor = BLACK;
	return true;
}

void RotateL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;

	//subR变成新的子树的头,parent链接到subR左边,并把subRL链接到自己右边
	subR->_left = parent;
	parent->_right = subRL;
	if (subRL)
		subRL->_parent = parent;

	Node* parentParent = parent->_parent;
	parent->_parent = subR;
	if (parentParent == nullptr)
	{
		_root = subR;
		subR->_parent = nullptr;
	}
	else
	{
		subR->_parent = parentParent;
		if (parentParent->_left == parent)
		{
			parentParent->_left = subR;
		}
		else if (parentParent->_right == parent)
		{
			parentParent->_right = subR;
		}
		else
		{
			assert(false);
		}
	}
}
void RotateR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;

	//subL变成新的子树的头,parent链接到subL右边,并把subLR链接到自己左边
	subL->_right = parent;
	parent->_left = subLR;
	if (subLR)
		subLR->_parent = parent;

	Node* parentParent = parent->_parent;
	parent->_parent = subL;
	if (parentParent == nullptr)
	{
		_root = subL;
		subL->_parent = nullptr;
	}
	else
	{
		subL->_parent = parentParent;
		if (parentParent->_left == parent)
		{
			parentParent->_left = subL;
		}
		else if (parentParent->_right == parent)
		{
			parentParent->_right = subL;
		}
		else
		{
			assert(false);
		}
	}
}

红黑树的验证

  1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
void InOrder()
{
	_InOrder(_root);
	cout << endl;
}

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

	_InOrder(root->_left);
	cout << root->_kv.first << " ";
	_InOrder(root->_right);
}
  1. 检测其是否满足红黑树的性质

红黑树的性质

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
bool Check(Node* root,int sum,int standval)
{
	if (root == nullptr)
	{
		if (sum != standval)
		{
			cout << "路径上的黑色节点个数不相同" << endl;
			return false;
		}
		return true;
	}
	//如果红色孩子的父亲是红色,说明违反了规则
	if (root->_cor == RED && root->_parent->_cor == RED)
	{
		cout << "出现重复的红色节点" << endl;
		return false;
	}
	//如果当前节点是黑色,当前路径黑节点个数加一
	if (root->_cor == BLACK)
		sum++;
		
	return Check(root->_left,sum, standval)
		&& Check(root->_right,sum, standval);
}

bool IsBalance()
{
	if (_root == nullptr)
		return true;
	if (_root->_cor == RED)
		return false;
	//判断一个红色节点的孩子是不是都是黑色
	//判断每个节点的黑色节点的个数,用标准值去比较
	int standval = 0;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_cor == BLACK)
		{
			standval++;	
		}
		cur = cur->_left;
	}
	int sum = 0;
	return Check(_root,sum,standval);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

二进制原码、反码、补码、移码

机器数&#xff1a;一个数在计算机中的二进制表示形式&#xff0c;称为这个数的机器数。符号位&#xff1a;机器数是带符号的&#xff0c;在计算机中用最高位作为符号位&#xff0c;0为正数&#xff0c;1为负数。真值&#xff1a;机器数由于含有符号位&#xff0c;所以机器数的…

搭建项目环境,集成ts和jest

前言 开新坑。 斥巨资购入大崔哥的 mini-vue 课程&#xff0c;为了改变自己东一榔头西一棒槌的学习状态&#xff0c;也是因为深刻思考了自己身无长物浑浑噩噩这么多年只会敲代码&#xff0c;别无出路&#xff0c;也只能提升自己继续走技术这条路&#xff0c;那提高技术绕不过…

6.HTML中表格标签

6.表格标签 表格是实际开发中非常常用的标签 6.1 表格的主要作用 表格主要用于显示、展示数据&#xff0c;因为它可以让数据显示的非常规整&#xff0c;可读性非常好。特别是后台展示数据的时候&#xff0c;能够熟练运用表格就显得十分重要。一个清爽简约的表格能够把繁杂的数据…

维基百科是非营利性机构 词条内容具有中立性、准确性、可靠性

维基百科对一些企业很有神秘性&#xff0c;自行操作很多次也没有成功建立维基百科&#xff0c;这一定是没有按照维基百科的规则和流程去操作。小马识途营销顾问提醒企业&#xff0c;维基百科是一种基于协作的在线百科全书&#xff0c;由维基媒体基金会运营。维基百科的创建流程…

React Virtual DOM及Diff算法

JSX到底是什么 使用React就一定会写JSX&#xff0c;JSX到底是什么呢&#xff1f;它是一种JavaScript语法的扩展&#xff0c;React使用它来描述用户界面长成什么样子&#xff0c;虽然它看起来非常像HTML&#xff0c;但他确实是javaScript&#xff0c;在React代码执行之前&#…

上海亚商投顾:沪指震荡反弹 鸿蒙、算力概念股集体爆发

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指昨日窄幅震荡&#xff0c;创业板指冲高回落&#xff0c;市场热点继续轮动。华为鸿蒙概念股继续活跃&#…

“具有分布式能源资源的多个智能家庭的能源管理的联邦强化学习”文章学习一

一、摘要 本文提出了一种新型的联邦强化学习&#xff08;FRL&#xff09;方法&#xff0c;用于管理带有家电、太阳能光伏系统和储能系统的多个智能家庭的能源。 所提出的FRL方法的创新点在于开发了一种由本地家庭能源管理系统(LHEMS)和全局服务器(GS)组成的分布式深度强化学习(…

.net core中前端vue HTML5 History 刷新页面404问题

放到启动的应用程序的最后面 app.Run(async (context) > {context.Response.ContentType "text/html";await context.Response.SendFileAsync(Path.Combine(env.WebRootPath, "index.html")); });https://blog.csdn.net/lee576/article/details/88355…

实现线程的多种方式锁的介绍ThreadLocal线程池 详细总结(下)

本文主要介绍线程池的基本使用 上述其他介绍在上一篇文章中&#xff1a;实现线程的多种方式&锁的介绍&ThreadLocal&线程池 详细总结&#xff08;上&#xff09;-CSDN博客 线程池 5.1、为什么使用线程池 线程池可以看做是管理了 N 个线程的池子&#xff0c;和连…

Java的XWPFTemplate word生成列表

Java的XWPFTemplate工具类导出word.docx的使用_xwpftemplate 语法_youmdt的博客-CSDN博客 如果是表格的列表参考上面这篇文章即可&#xff0c;比较复杂的列表遍历暂时还没找到方法&#xff0c;只能手动创建表格了 上面是模板&#xff0c;非常简单&#xff0c;以为我们是要自己创…

高效免费办公神器——ONLYOFFICE入手指南

前言&#xff1a; 作为开发者&#xff0c;有时候经常为寻找适合的开发工具而苦恼&#xff1b;或者因为高昂的费用而犹豫不决&#xff1b;亦或喜欢的办公产品只能在单一的平台上使用&#xff0c;与其把时间花在复杂的工具使用上&#xff0c;不如节省出时间投入思考和技术的提升。…

云课五分钟-01课程在哪里-无需安装网页直达

此部分课程均为2015-2019年规划和设计&#xff0c;2020-2022年新版课程还在内测中。 现在想想当年还是很莽的&#xff0c;总想着一个网页云服务&#xff0c;把机器人相关不涉及硬件的课程全囊括。 无需安装个性定制即开即用随时随地云端复现…… 视频 云课五分钟-01课程在哪…

C++设计实现日志系统

转载&#xff1a;C设计实现日志系统 - 知乎 (zhihu.com) 日志系统几乎是每一个实际的软件项目从开发、测试到交付&#xff0c;再到后期的维护过程中极为重要的 查看软件代码运行流程、 还原错误现场、 记录运行错误位置及上下文等的重要依据。一个高性能的日志系统&#xff0c…

【ArcGIS Pro微课1000例】0032:创建具有指定高程Z值的矢量数据

本文讲解ArcGIS Pro中创建具有指定高程值的矢量数据的两种方法。 文章目录 一、独立创建1. 新建地图场景2. 新建shapefile3. 绘制多边形4. 添加高程字段5. 三维显示二、基于高程源创建1. 创建栅格范围2. 添加Z值字段3. 添加Z信息4. 要素更新Z值一、独立创建 1. 新建地图场景 …

Pytorch多GPU并行训练: DistributedDataParallel

1 模型并行化训练 1.1 为什么要并行训练 在训练大型数据集或者很大的模型时一块GPU很难放下&#xff0c;例如最初的AlexNet就是在两块GPU上计算的。并行计算一般采取两个策略&#xff1a;一个是模型并行&#xff0c;一个是数据并行。左图中是将模型的不同部分放在不同GPU上进…

【vue】AntDV组件库中a-upload实现文件上传:

文章目录 一、文档&#xff1a;二、使用(以Jeecg为例)&#xff1a;【1】template&#xff1a;【2】script&#xff1a; 三、效果图&#xff1a; 一、文档&#xff1a; Upload 上传–Ant Design Vue 二、使用(以Jeecg为例)&#xff1a; 【1】template&#xff1a; <a-uploa…

Springboot项目返回数据统一封装

Springboot项目返回数据统一封装,支持swagger。 正常swagger会根据数据库表的注释显示对应的参数释义等。但当我们使用统一接口返回map时&#xff0c;部分注释等信息会被掩盖消失。在此提供三个java类即可满足统一封装返回接口&#xff0c;也可显示对应的swagger释义等。 1.Er…

Vue 2学习(路由、history 和 hash 模式、)-day014

一、路由简介 路由&#xff08;route&#xff09;就是一组 key-value 的对应关系多个路由&#xff0c;需要经过路由器&#xff08;router&#xff09;的管理 在 Vue 中也有路由&#xff0c;Vue 中的路由主要是通过 vue-rounter 这个插件库来实现&#xff0c;它的作用就是专门用…

php 插入排序算法实现

插入排序是一种简单直观的排序算法&#xff0c;它的基本思想是将一个数据序列分为有序区和无序区&#xff0c;每次从无序区选择一个元素插入到有序区的合适位置&#xff0c;直到整个序列有序为止 5, 3, 8, 2, 0, 1 HP中可以使用以下代码实现插入排序算法&#xff1a; functi…

【考研复习】二叉树的特殊存储|三叉链表存储二叉树、一维数组存储二叉树、线索二叉树

文章目录 三叉链表存储二叉树三叉链表的前序遍历&#xff08;不使用栈&#xff09;法一三叉链表的前序遍历&#xff08;不使用栈&#xff09;法二 一维数组存储二叉树一维数组存储二叉树的先序遍历 线索二叉树的建立真题演练 三叉链表存储二叉树 三叉链表结构体表示如下图所示…