【数据结构】红黑树模拟实现

2023-11-14

一.红黑树底层原理

红黑树的底层可以看作是AVL树的变种。先前我们了解过AVL的模拟实现。avl对整棵树的控制还是非常严格的,因为高度差不能大于2,导致会经常发生旋转。旋转这个过程也会降低效率,所以为了整体的效率衍生出了红黑树。红黑树旋转的没有那么频繁。红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

红黑树的节点没有平衡因子,由颜色替代,红色和黑色。红黑树的实现是由四条规则实现的

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

当满足这四条规则时,这棵树自然而然就是红黑树了。因为每条路径的黑色节点数是相同的,所以不同路径的节点数的差别一定在于红色节点数的不同。同时又因为红色节点不相邻,所以一条路径上最少可以有0个黑色节点,最多可以有黑色节点数个红色节点。这就控制了有一条路径会比其他路径长出俩倍。

二.红黑树的节点结构

其实本质上和avl树的节点没有任何不同,就是把平衡因子去点换成颜色


enum Colour
{
	RED,
	BLACK
};

template <class T>
struct RBTreeNode
{
	RBTreeNode(const T& data = T())
		: _pLeft(nullptr)
		, _pRight(nullptr)
		, _pParent(nullptr)
		, _data(data)
	{
	}

	RBTreeNode<T>* _pLeft;
	RBTreeNode<T>* _pRight;
	RBTreeNode<T>* _pParent;
	T _data;
	Colour _col;  
};

三.红黑树的插入

由于节点都有颜色所以我们在插入新的节点的时候首先要确定新节点是什么颜色的。这里我们采取插入节点为红色的策略,因为插入红色节点相对简单些。当我们插入一个新节点后,我们需要判断当前树是否还满足规则。若当前根节点为空的话可以直接让当前节点作为根节点。若不为空的话则和avl树一样先找到应该插入的位置,构建插入节点并连接指针的指向。唯一的区别是处理颜色和是否需要旋转的条件

先说parent在grand左边的情况。此时包含三种情形

第一种:uncle存在为红。这种情况不需要进行旋转,只需要进行变色。假设新增节点是D,此时不满足红黑树条件,uncle节点(也就是C)存在且是红色,此时我们让父节点(也就是D)和uncle节点变黑,grandparent(A)变红。不断向上传递颜色直到到根节点位置。最后把根节点变黑。

 变色到最后是这个样子

 第二种:当uncle节点不存在或者uncle为黑色。且新增节点在parent左侧的时候。需要进行变色加单旋,假设新增节点是D,此时相当于新增在较高左子树的左边,我们需要进行一个右单旋

 旋转完成后在进行变色,把parent变黑(parent有可能成为根节点)把grand变红

 

 这样就满足红黑树的条件了

 第三种:当uncle节点不存在或者uncle为黑色。且新增节点在parent右侧的时候。需要进行一个双旋。新增节点为e

 我们可以先对D进行一个左单旋,此时变成了和情况二一样的情形,再对B进行一个右单旋

 最后把新增节点变黑grand节点变红

 就满足红黑树条件了

 上述三种情况是parent在grand的左测情况,同理还有parent在grand右侧的情况。旋转方式相反思路相同。

	pair<iterator, bool> Insert(const T& data)
	{
		KeyOfT kot;
		if (_root == nullptr)  //处理当树为空时
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root),true);
		}
		Node* parent = nullptr;
		Node* cur = _root;
		//寻找插入位置
		while (cur)
		{
			if (kot(cur->_data) < kot(data))
			{
				parent = cur;
				cur = cur->_pRight;
			}
			else if (kot(cur->_data) > kot(data))
			{
				parent = cur;
				cur = cur->_pLeft;
			}
			else
			{
				return make_pair(iterator(cur), false);
			}
		}

		cur = new Node(data);  //找到位置插入节点
		cur->_col = RED;		//新插入的节点置为红色
		Node* newnode = cur;

		cur->_pParent = parent;  //调整新插入节点的两个指针指向。
		if (kot(parent->_data) < kot(data))
		{
			parent->_pRight = cur;
		}
		else if (kot(parent->_data) > kot(data))
		{
			parent->_pLeft = cur;
		}

		while (parent && parent->_col == RED)
		{
			Node* grandfater = parent->_pParent;   //保存祖父节点
			if (parent == grandfater->_pLeft)		//当前节点是祖父的左节点
			{
				Node* uncle = grandfater->_pRight;		//说明叔叔节点数祖父的右节点
				//uncle存在且为红色,继续向上变色
				if (uncle && uncle->_col == RED)
				{
					uncle->_col = BLACK;			//父亲和叔叔变黑,爷爷变红
					parent->_col = BLACK;
					grandfater->_col = RED;

					cur = grandfater;				//继续向上传递
					parent = cur->_pParent;			
				}
				//uncle不存在+uncle存在且为黑色  (需要进行变色+旋转,若遇到折线情况需要进行双旋)
				else
				{
					if (cur == parent->_pLeft)   //相当于新增节点在较高左子树的左边,右单旋即可
					{
						RotateR(grandfater);
						grandfater->_col = RED;
						parent->_col = BLACK;
					}
					else   //相当于新增节点在较高左子树的右侧,形成折线需要进行双旋转,先左旋在右旋
					{
						RotateL(parent);
						RotateR(grandfater);
						grandfater->_col = RED;
						cur->_col = BLACK;
					}
					break;
				}
			}
			else if (parent == grandfater->_pRight)
			{
				Node* uncle = grandfater->_pLeft;
				//uncle存在且为红色,继续向上变色
				if (uncle && uncle->_col == RED)
				{
					uncle->_col = BLACK;			//父亲和叔叔变黑,爷爷变红
					parent->_col = BLACK;
					grandfater->_col = RED;

					cur = grandfater;
					parent = cur->_pParent;
				}
				//uncle不存在+uncle存在且为黑色  (需要进行变色+旋转,若遇到折线情况需要进行双旋)
				else
				{
					if (cur == parent->_pRight) 
					{
						RotateL(grandfater);
						grandfater->_col = RED;
						parent->_col = BLACK;
					}
					else   
					{
						RotateR(parent);			 //当前情况相当于新增节点在较高右子树的左侧
						RotateL(grandfater);
						grandfater->_col = RED;
						cur->_col = BLACK;
					}
					break;
				}
			}
		}
		_root->_col = BLACK;   //最后把根节点的颜色赋值成黑色
		return make_pair(iterator(newnode), true);  //不能直接使用cur,cur有可能已经被更改
	}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【数据结构】红黑树模拟实现 的相关文章

随机推荐

  • BZOJ4817 [Sdoi2017]树点涂色(洛谷P3703)

    LCT 线段树 BZOJ题目传送门 洛谷题目传送门 码力不行啊 操作1就是access啦 操作2就是 w x w y 2 w lca x y 1 w x w y 2
  • 利用Libev写一个简单的client和server程序

    利用Libev写一个简单的client和server程序 common h程序 ifndef COMMON H define COMMON H include
  • 15 移动端app自动化测试 - 软件测试

    软件测试所有内容笔记正在陆续更新中 笔记已经在本地记录 全部为自己手动记录的笔记及总结 正在开始更新中 后续会逐步更新并完善到 软件测试学习内容总结 专栏 本节内容 移动端app自动化测试 文章目录 1 appium环境安装与架构介绍 1
  • linux常用命令:文本编辑

    目录 三 文本编辑 1 vim三种工作模式 2 常用命令 3 插入文本快捷键 4 查找文本快捷键 5 查找文本效果图 6 替换文本快捷键 7 删除文本快捷键 8 复制和粘贴文本快捷键 9 保存退出文本命令 10 参考文章 三 文本编辑 一般
  • 二值分类模型的评价指标

    二值分类模型的评价指标主要有 Precision Recall F Score ROC and AUC ROC Receiver Operating Characteristic ROC曲线的横坐标为false positive rate
  • linux gsoap服务端,gsoap linux下服务器端代码生成与测试

    二 解压压缩包 ios 三 进入cd gSoap gsoap 2 8 gsoap bin linux386函数 四 建立一个头文件serverTest h测试 gsoap ns service name calc gsoap ns serv
  • Python selenium错误:ElementNotInteractableException: Message: element not interactable: Element is not

    脚本没问题 但是执行时报错 selenium common exceptions ElementNotInteractableException Message element not interactable Element is not
  • [Python图像处理] 五.图像融合、加法运算及图像类型转换

    该系列文章是讲解Python OpenCV图像处理知识 前期主要讲解图像入门 OpenCV基础用法 中期讲解图像处理的各种算法 包括图像锐化算子 图像增强技术 图像分割等 后期结合深度学习研究图像识别 图像分类应用 希望文章对您有所帮助 如
  • javascript的深拷贝和浅拷贝

    当我们将一个变量的值赋给另外一个变量时 需要注意用的是深拷贝还是浅拷贝 深拷贝是另外开辟地址空间 浅拷贝是和被拷贝数据地址一样 详见 https github com YvetteLau Step By Step issues 17
  • element-ui 的confirm用法

    在点击函数里变嵌入一下代码 this confirm 确认删除 系统提示 confirmButtonText 确定 cancelButtonText 取消 cancelButtonClass btn custom cancel type w
  • 解决websocket使用@Autowired、@Value获取值为null解决方法

    解决webSocker中使用 Value获取配置文件中值为null 1 常规写法 在webSocker中使用 Value 取值为null 2 原因分析 3 解决问题 1 常规写法 在webSocker中使用 Value 取值为null Co
  • 浏览器渲染原理

    页面渲染流程 浏览器收到web网页的内容后会开始下载 HTML CSS JS 并绘制 DOM 树和 CSS 树 当两颗树都绘制完成后会合并成一个渲染树 然后再经过Layout Paint Composite 最终完成渲染 HTML 解析会被
  • hortonworks/registry配置详解

    1 美图 2 配置 deploy demo db registry cat conf registry yaml registries configuration modules name schema registry className
  • 什么是CSRF攻击,以及如何防御

    什么是CSRF攻击 以及如何防御 1 CSRF攻击的概念 2 CSRF攻击简单案例 2 1 银行网站项目 2 2 危险网站的项目 2 3 测试 3 默认的CSRF防御策略 4前后端分离的CSRF防御策略 1 CSRF攻击的概念 1 CSRF
  • MPI群通信与矩阵乘法的Fox算法实现

    原本以为 MPI天生只能在Linux上运行 但这次却发现了Intel MPI Library 这个好用的东西 基本不需要设置 安上之后 用自己能登录windows的帐号和密码注册就行了 虽然不是局域网上的机器 但也可以让我的双核CPU达到1
  • WebSocket协议之NGINX代理转发无法建立连接问题处理

    WebScoket协议如需要通过nginx代理 需要location 节点增加以下节点即可正常建立连接 需要配置以下节点 proxy http version 1 1 proxy set header Upgrade http upgrad
  • Python入门网络爬虫之精华版,赶快收藏

    相关文件 关注小编 私信小编领取哟 当然别忘了一件三连哟 公众号 Python日志 前言 Python学习网络爬虫主要分3个大的版块 抓取 分析 存储 另外 比较常用的爬虫框架Scrapy 这里最后也详细介绍一下 举例子 当我们在浏览器中输
  • STL 中集合操作相关算法

    merge 头文件 merge 算法定义在头文件 include 中 算法作用 merge 算法是合并两个有序的序列 合并结果拷贝到一个新的序列 前提是这两个序列的排序规则一样 代码示例 vector
  • vue---UI框架elementUI实现系统登录注册页

    https blog csdn net maidu xbd article details 87943243已经搭建好了vue开发环境 在本博客中 来介绍些结合element ui实现登录注册界面 界面效果展示如下图 实现的功能包括 首先安
  • 【数据结构】红黑树模拟实现

    一 红黑树底层原理 红黑树的底层可以看作是AVL树的变种 先前我们了解过AVL的模拟实现 avl对整棵树的控制还是非常严格的 因为高度差不能大于2 导致会经常发生旋转 旋转这个过程也会降低效率 所以为了整体的效率衍生出了红黑树 红黑树旋转的