【C++红黑树】带图详细解答红黑树的插入,测试自己的红黑树是否正确的代码

news2024/11/29 14:51:51

目录

1.红黑树的概念

1.1红黑树的特性(4+1)

2.红黑树的框架 

3.红黑树的插入 

        3.1parent在grandfather的左边

         3.1parent在grandfather的右边

 4.测试自己的红黑树是不是平衡的


1.红黑树的概念

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

  • 所以它是一个弱平衡二叉搜索树,AVL1树是一个严格的平衡二叉搜索树

1.1红黑树的特性(4+1)

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

每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

  • 我认为这一条只是标记的作用,让我们更好分别每一条路径

 

2.红黑树的框架 

//枚举颜色
enum Colour
{
	RED,
	BLACK,
};
template<class K, class V>
struct RBtreeNode
{
	RBtreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
        //初始化给红色,红色比黑色更好处理
		,_col(RED)
	{}
	//三叉链
	RBtreeNode<K, V>* _left;
	RBtreeNode<K, V>* _right;
	RBtreeNode<K, V>* _parent;
	//数据
	pair<K,V> _kv;
	//颜色
	Colour _col;
};
template<class K,class V>
class RBtree
{
	typedef RBtreeNode<K,V> Node;
public:
	RBtree()
		:_root(nullptr)
	{}
	
    //旋转
	void RotateL(Node* parent)
	void RotateR(Node* parent)
    //插入
	pair<Node*, bool> Insert(const pair<K, V> kv)
    //寻找
	Node* Find(const K& key)
    //测试自己的写的的红黑树,是否合适
	bool CheckBalance()

private:
	Node* _root;

};

3.红黑树的插入 

pair<Node*, bool> Insert(const pair<K, V> kv)
    //是否为空树
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			return make_pair(_root, true);
		}
		Node* cur = _root,*parent=_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 make_pair(cur, false);
			}
		}
		Node* newnode = new Node(kv);
		newnode->_col = RED;
		if (parent->_kv.first < kv.first)
		{
			parent->_right = newnode;
			newnode->_parent = parent;
		}
		else
		{
			parent->_left = newnode;
			newnode->_parent = parent;
		}
		cur = newnode;
		while (parent && parent->_col == RED)
		{
			// 如果父亲存在,且颜色为红色就需要处理
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				// 关键是看叔叔
				Node* uncle = grandfather->_right;
				// 情况1:uncle存在且为红
				if (uncle&&uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else// 情况2+3:uncle不存在 uncle存在且为黑
				{
					// 情况2:单旋
					if (cur == parent->_left)
					{
						RotateR(grandfather);

						parent->_col = BLACK ;
						grandfather->_col = RED;
					}
					// 情况3:双旋
					else
					{
						RotateL(parent);
						RotateR(grandfather);

						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					//最上面的节点已经变黑了,不用继续
					break;
				}
			}
			// 如果父亲存在,且颜色为红色就需要处理
			else
			{
				// 关键是看叔叔
				Node* uncle = grandfather->_left;
				// 情况1:uncle存在且为红
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2 + 3:uncle不存在 uncle存在且为黑
				{
					// 情况2:单旋
					if (cur == parent->_right)
					{
						RotateL(grandfather);

						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					// 情况3:双旋
					else
					{
						RotateR(parent);
						RotateL(grandfather);

						cur->_col = BLACK;
						grandfather->_col = RED;
					}
				}
				break;
			}
		}
		_root->_col = BLACK;
		return make_pair(newnode, true);
	}

 插入整体逻辑:

  1. 如果还没有元素是一课空树,直接插入即可;如果有元素,按pair的first(key)和比较的节点比较结果为大说明为空的那个位置在右边,和比较的节点比较的结果小说明为空的哪个位置在左边;如果相等说明已经有这个元素了,二叉搜索树不支持冗余,插入失败则,返回一个pair类第一个成员为那个相同元素的map的迭代器和第二个成员为false的pair类迭代器;
  2. 不知道这个已经找到的位置在父节点的左边还是右边,需要判断一下,然后插入元素;
  3. 考虑变色

3.1不平衡处理

如果有父亲且父亲为红色说明不平衡,就一直向上调整,直到cur到头节点或者parent为黑色

1.第一种情况:有uncle并且uncle为红色处理:parent和uncle变为黑色,grandfather变红色

  • 一直向上调整可能会让头节点变红,那么就在循环外把头节点处理一下

 2.第二种情况,没有uncle或者有uncle且为黑色,有uncle一定是第一种情况变化而来

3.1parent在grandfather的左边

这种情况单纯的变色已经做不到平衡了,怎么办?

旋转处理:parent在grandfather的左边,右单旋和左右双旋

 3.1parent在grandfather的右边

  • 逻辑和在左边是一样的,大家可以自己尝试画一下
  • 旋转我在AVL树右详细解答http://t.csdn.cn/AlRzI

旋转代码:

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

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

		subR->_left = parent;
		Node* parentParent = parent->_parent;
		parent->_parent = subR;

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

	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

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

		subL->_right = parent;
		Node* parentParent = parent->_parent;
		parent->_parent = subL;

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

			subL->_parent = parentParent;
		}
	}

 4.测试自己的红黑树是不是平衡的

  • 测试了头节点是不是黑色,是否有连续的红节点,每条路径上的黑节点
bool _CheckBalance(Node* root,int LeftNum,int count)
	{
		if (root == nullptr)
		{
			if (count != LeftNum)
			{
				return false;
			}
			return true;
		}
		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << "存在连续的红色节点" << endl;
			return false;
		}
		if (root->_col == BLACK)
		{
			count++;
		}
		return _CheckBalance(root->_left, LeftNum, count) && 
			_CheckBalance(root->_right, LeftNum, count);
	}
	bool CheckBalance()
	{
		if (_root == nullptr)
		{
			//空树是红黑树
			return true;
		}
		else if(_root->_col==RED)
		{
			cout << "根节点是红色的" << endl;
			return false;
		}
		else
		{
			int LeftNum = 0;
			Node* left = _root;
			// 找最左路径做黑色节点数量参考值
			while (left)
			{
				if (left->_col == BLACK)
				{
					LeftNum++;
				}
				left = left->_left;
			}
			int count = 0;
			return _CheckBalance(_root, LeftNum, count);
		}
	}

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

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

相关文章

米联客FDMA3.1数据缓存方案全网最细讲解,自创升级版,送3套视频和音频缓存工程源码

米联客的FDMA数据缓存方案发布也有五六年了&#xff0c;但真正能熟练使用的兄弟却很少&#xff0c;其实还是没有好的例程作为参考和同熟易懂的讲解&#xff0c;这里我做如下解析&#xff1a; FDMA部分&#xff1a;这部分是米联客封装了用户接口的AXI4-FULL协议代码&#xff0c;…

xhs-web校验流程分析

经测试&#xff0c;cookie中需携带gid和timestamp2。参数整理有点乱&#xff0c;仅供参考。 xhsFingerprintV3&#xff0c;VERSION: ‘2.1.2’ 文章目录流程概述timestamp2滑块验证参数Params轨迹FNcaptcha deviceIdProfileDatax-s-commonx5生成x8生成x9生成smidV2a1x-b3-trac…

Jenkins配置linux节点

之前在Windows下安装Jenkins 但是通过windows节点进行构建有诸多的不便&#xff0c;于是想到通过Jenkins里添加linux节点&#xff0c;让构建的时候&#xff0c;使用远程的linux服务器构建 目录一、配置凭据二、配置节点一、配置凭据 Manage Jenkins → Manage Credentials→Je…

Cadence Allegro PCB设计88问解析(十三) 之 Allegro中artwork层的建立

一个学习信号完整性的layout工程师 作为layout工程师&#xff0c;我们经常接触到的是PCB文件&#xff0c;用Cadence设计的是.brd文件。但是我们发给板厂的都是gerber文件。这就涉及到在我们设计好PCB文件之后&#xff0c;怎么把这些文件给到板厂。也就是我们Allegro中的artwork…

Java知识点--IO流(上)

Java知识点--IO流&#xff08;上&#xff09;一、文件1、文件的含义2、文件流二、常用的文件操作1、创建文件对象相关构造器和方法2、创建文件案例演示&#xff08;三种创建方法&#xff09;3、获取文件相关信息的方法4、获取文件相关信息方法案例演示5、目录的操作与删除6、应…

电脑系统重装下载的系统在哪找到

​因为现在很多人都在使用小白一键重装系统&#xff0c;但是很多人都不太能够了解小白系统重装下载的系统在哪儿&#xff0c;下面是小编提供的具体位置供大家参考。 工具/原料&#xff1a; 系统版本&#xff1a;win10 品牌型号&#xff1a;联想yoga13 软件版本&#xff1a;小白…

期货开户有什么规定

有很多朋友刚刚接触到资本市场&#xff0c;当他们听到期货时&#xff0c;他们觉得它非常遥远和高端&#xff0c;这是普通人无法接触到的。但事实上&#xff0c;情况并非如此。期货是一种非常普通的金融产品&#xff0c;很容易接触。让我们来看看期货开户有哪些必要条件以及有什…

postman环境变量的设置

背景&#xff1a;由于我们项目接口入参都有加密&#xff0c;每次接口调试都得启动项目&#xff0c;运行项目才能把对应的参数给传到后台&#xff0c;然后后台再解密参数&#xff0c;才能进行接口调试&#xff0c;很麻烦&#xff08;启动前端项目&#xff09;&#xff0c;如果接…

Linux篇【3】:Linux环境基础开发工具使用(中)

目录 一、Linux 编译器&#xff1a;gcc/g 的使用 1.1、知识拓展&#xff1a; 1.2、如何安装 C/C 标准静态库&#xff1a; 1.3、头文件与库文件&#xff1a; 1.4、静态库&#xff0c;静态链接&#xff0c;动态库&#xff0c;动态链接&#xff1a; 二、简单 vim 配置 2.1、…

【SpringBoot】一文了解SpringBoot热部署

文章目录前言手动启动热部署热部署种类手动进行热部署自动启动热部署热部署范围配置热部署的关闭总结&#x1f315;博客x主页&#xff1a;己不由心王道长&#x1f315;! &#x1f30e;文章说明&#xff1a;一文彻底搞懂SpringBoot热部署&#x1f30e; ✅系列专栏&#xff1a;Sp…

深入理解Java虚拟机:Java类的加载机制

本篇内容包括&#xff1a;Java 类的加载机制&#xff08;Jvm 结构组成、Java 类的加载&#xff09;、类的生命周期&#xff08;加载-验证-准备-解析-初始化-使用-卸载&#xff09;、类加载器 以及 双亲委派模型。 一、Java 类的加载机制 1、 Jvm 结构组成 Jvm 整体组成可分为…

坤坤音效键盘(Python实现)

文章目录坤坤音效键盘说明坤坤音效键盘效果展示代码实现安装第三方库准备音频监听键盘播放音频编写逻辑引入线程打包成exe程序坤坤音效键盘说明 坤坤音效键盘说明&#xff1a; 单独按下 j、n、t、mj、n、t、mj、n、t、m 按键&#xff0c;会对应触发 “鸡”、“你”、“太”、…

科技视界杂志科技视界杂志社科技视界编辑部2022年第21期目录

科技视界杂志科技视界杂志社科技视界编辑部2022年第21期目录 科普论坛《科技视界》投稿&#xff1a;cnqikantg126.com 天敌昆虫——让农业生产更安全 季香云; 1-3 储粮昆虫三维模型Web可视化技术研究与应用 阎磊;马宏琳;李亮;李鹏翔;王义超; 4-6 科学实验 非均匀催…

wy的leetcode刷题记录_Day33

wy的leetcode刷题记录_Day33 时间&#xff1a;2022-11-4 目录wy的leetcode刷题记录_Day33754. 到达终点数字题目介绍思路代码收获199. 二叉树的右视图题目介绍思路代码收获754. 到达终点数字 今天的每日一题是&#xff1a;754. 到达终点数字 题目介绍 在一根无限长的数轴上…

CSS:变量函数var和自定义属性

文章目录CSS变量var()函数CSS变量 CSS变量分为两部分&#xff1a;变量声明和变量使用。 变量的声明是由CSS自定义属性和对应的属性朱组成的&#xff0c;比如&#xff1a; :root {--custom-color: deepskyblue;}在这段代码中&#xff0c;–custom-color是属于css的自定义属性名…

方法的使用

目录 1. 举例说明什么叫方法 2. 方法概念及使用 2.1 什么是方法(method) 1.2 方法定义 1.3 方法调用的执行过程 1.4 实参和形参的关系(重要) 2. 方法重载 2.1 为什么需要方法重载 2.3 方法签名 3. 递归 递归执行过程分析 1. 举例说明什么叫方法 我们利用面向对象的方…

2022年特色小镇行业研究报告

第一章 行业概况 特色小镇是在几平方公里土地上集聚特色产业、生产生活生态科技相融合、不同于行政建制镇和产业园区的创新创业平台。根据类型的不同&#xff0c;特色小镇可以分为三类&#xff0c;即产业类、社区类和旅游类。 产业类&#xff1a;通过招商引资吸引企业进入&…

门控循环单元(GRU)【动手学深度学习v2】

理论 候选隐藏状态。 圆圈 表示 按元素乘法。 这里面的 这个符号值得是 按元素相乘。 Rt理解为 和Ht 长度一样的一维向量。&#xff08;这么理解&#xff09; 这里如果Rt长的像0的话&#xff0c;那么乘出来的结果&#xff0c;就也像0。 要是像0 的话&#xff0c;相当于是说…

文件操作之文件系统

目录 一 磁盘 1 磁盘的物理结构 2 磁盘在物理结构上如何存储数据 CHS寻址 3 从物理结构到抽象结构 LBA寻址 4管理 二 块组 boot block super block inode table data blocks block bitmap GDT&#xff1a;Global Descriptor Table 块组描述符 三 文件名和目录之…

网课查题接口搭建

网课查题接口搭建 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点击跳…