【C++】红黑树的概念与模拟实现

news2024/11/18 3:31:51

红黑树的概念与模拟实现

  • 红黑树的概念
  • 红黑树的性质
  • 红黑树节点的定义
  • 红黑树的迭代器
  • 红黑树的插入
  • 红黑树和AVL树的比较
  • 红黑树的模拟实现

红黑树的概念

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

红黑树的性质

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

红黑树节点的定义

enum Color
{
	RED, BLACK
};

template<class T>
struct RBTreeNode
{
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;

	T _data;
	Color _color;

	RBTreeNode(const T& data = T(), Color color = RED)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_data(data)
		,_color(color)
	{}
};

红黑树的迭代器

首先我们来看下红黑树中的迭代器应该是在什么位置
在这里插入图片描述

红黑树的结构中,有一个根节点,也有一个头节点,头节点是不负责存储数据的,而根节点才是常规意义上的二叉树的根节点,而红黑树中,begin()在头节点的左子树,也就是最左侧的节点(因为红黑树也是二叉平衡树),最左侧的节点就是最小的节点,end()在头节点的位置,因为如果对end()减减时,需要让迭代器走到最后一个节点的位置,也就意味着end应该指向最后一个节点的下一个位置。

首先,不关注红黑树节点中的值,因为作为迭代器,并不会对红黑树的结构进行修改,也就不会破坏红黑树,所以可以忽略红黑树的性质,只去关注它作为一棵二叉平衡树的特点。

核心的代码就变成了如何对迭代器实现Increment和Decrement操作,也就是加加减减操作

红黑树的插入

红黑树的插入实际上是分两个步骤的: 第一步,先用插入二叉搜索树的方式进行插入: 先拿到当前树的根节点,判断当前树是否为空
1.若为空,则创建一个根节点,作为头节点的parent,根节点和头节点互为parent
2.若不为空,从根节点开始,找待插入的节点位置
3.找到插入位置后,插入节点(默认的插入颜色是红色)
4.需要检测插入节点后,红黑树的性质是否被破坏了
5.检测完毕,并且进行修改
6.修改根节点的颜色,并且修改头节点的左右指向

对于步骤4是最重要的一步,因为涉及到红黑树的性质被破坏后,如何进行修改
我们可以分几种情况来进行讨论:
注意:cur为当前节点,p为父节点, g为祖父节点,u为叔叔节点

情况1.cur为红,p为红,g为黑,u存在且为红

在这里插入图片描述

那么就需要将p和u的颜色改为黑,同时将g的颜色改为红。
1.这时候就需要看g是否是根节点了,如果是根节点,只需要将根节点修改为黑即可。
2.如果g不是根节点,那么就需要继续向上判断,如果g的双亲是黑,也不需要进行任何修改,但是如果g的双亲是红,就违反了性质3,需要继续向上修改----->相当于将g作为cur,继续向上调整

情况2.cur为红,p为红,g为黑,u不存在/u存在且为黑

对于第一种:u不存在的情况
在这里插入图片描述

可以推断出,cur一定是新插入的节点,因为如果cur不是新插入的节点,那么cur和p一定要有一个是黑色,不然就违反了性质4
对于第二种:u存在的情况
在这里插入图片描述
u一定是黑色的,cur节点原来的颜色也是黑色的,现在看到是红色是由于cur的子树在调整时将cur的颜色由黑色改为红色了。

对于这两种场景的解决方案是:
p为g的左孩子,cur为p的左孩子,则进行右单旋;
p为g的右孩子,cur为p的右孩子,则进行左单旋。

旋转完成后,p变黑,g变红。
在这里插入图片描述

在这里插入图片描述

场景3:cur为红,p为红,g为黑,u不存在/u存在且为黑 和场景2相似,区别在于场景3中cur的位置,
如果p在g的左侧,cur在p的右侧,也就是内侧。
如果p在g的右侧,cur在p的左侧,也就是内侧。

在这里插入图片描述

p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;
p为g的右孩子,cur为p的左孩子,则针对p做右单旋转
通过这样的旋转之后,情况3就变成了情况2,再对情况2进行处理

当然,上述的三种情况是指parent在祖父节点的左,还有三种情况是可能在祖父节点的右。和上述的情况刚好相反。

红黑树和AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( l o g 2 N log_2 N log2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

红黑树的模拟实现

#pragma once

#include <iostream>
using namespace std;
#include <functional>


enum Color
{
	RED, BLACK
};


template<class T>
struct RBTreeNode
{
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	T _data;
	Color _color;

	RBTreeNode(const T& data = T(), Color color = RED)
		: _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _color(color)
	{}
};

template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T, Ref, Ptr> Self;
public:
	RBTreeIterator(Node* pNode = nullptr)
		: _pNode(pNode)
	{}

	// 具有指针类似的操作
	Ref operator*()
	{
		return _pNode->_data;
	}

	Ptr operator->()
	{
		return &(operator*());
	}

	// 能够移动
	Self& operator++()
	{
		Increment();
		return *this;
	}

	Self operator++(int)
	{
		Self temp(*this);
		Increment();
		return temp;
	}

	Self& operator--()
	{
		Decrement();
		return *this;
	}

	Self operator--(int)
	{
		Self temp(*this);
		Decrement();
		return temp;
	}

	// 迭代器可以比较
	bool operator!=(const Self& s)const
	{
		return _pNode != s._pNode;
	}

	bool operator==(const Self& s)const
	{
		return _pNode == s._pNode;
	}

private:
	void Increment()
	{
		if (_pNode->_right)
		{
			_pNode = _pNode->_right;
			while (_pNode->_left)
			{
				_pNode = _pNode->_left;
			}
		}
		else
		{
			Node* parent = _pNode->_parent;
			while (_pNode == parent->_right)
			{
				_pNode = parent;
				parent = _pNode->_parent;
			}

			// 注意特殊情况:根没有右孩子场景
			if(_pNode->_right != parent)
				_pNode = parent;
		}
	}

	void Decrement()
	{
		if (_pNode->_parent->_parent == _pNode && RED == _pNode->_color)
		{
			_pNode = _pNode->_right;
		}
		else if (_pNode->_left)
		{
			_pNode = _pNode->_left;
			while (_pNode->_right)
			{
				_pNode = _pNode->_right;
			}
		}
		else
		{
			Node* parent = _pNode->_parent;
			while (_pNode == parent->_left)
			{
				_pNode = parent;
				parent = _pNode->_parent;
			}

			_pNode = parent;
		}

	}
private:
	Node* _pNode;
};

// 假设:key唯一
template<class T, class KOFD, class Compare = less<T>>
class RBTree
{
	typedef RBTreeNode<T> Node;

public:
	typedef RBTreeIterator<T, T&, T*> iterator;

public:
	RBTree()
		: _size(0)
	{
		_head = new Node();
		_head->_left = _head;
		_head->_right = _head;
	}

	~RBTree()
	{
		_DestroyRBTree(GetRoot());
		delete _head;
		_head = nullptr;
	}

	iterator begin()
	{
		return iterator(_head->_left);
	}

	iterator end()
	{
		return iterator(_head);
	}

	pair<iterator, bool> Insert(const T& data)
	{
		// 1. 二叉搜索树
		// 按照二叉搜索树的规则插入新节点
		// 空树
		Node*& root = GetRoot();
		Node* newNode = nullptr;
		if (nullptr == root)
		{
			newNode = root = new Node(data, BLACK);
			root->_parent = _head;
		}
		else
		{
			// 非空:
		// 找待插入节点在红黑树中的位置并保存其双亲
			Node* cur = root;
			Node* parent = _head;
			KOFD kofD;
			while (cur)
			{
				parent = cur;
				if (_com(kofD(data), kofD(cur->_data)))
					cur = cur->_left;
				else if (_com(kofD(cur->_data), kofD(data)))
					cur = cur->_right;
				else
					return make_pair(iterator(cur), false);
			}

			// 插入新节点
			newNode = cur = new Node(data);
			if (_com(kofD(data), kofD(parent->_data)))
				parent->_left = cur;
			else
				parent->_right = cur;
			cur->_parent = parent;


			// 2. 节点添加颜色 + 性质约束平衡性
			// 检测新节点插入后,红黑树的性质是否造到破坏
			while (_head != parent && RED == parent->_color)
			{
				// 违反性质三
				Node* grandFather = parent->_parent;
				if (parent == grandFather->_left)
				{
					// 课件中的三个场景:
					Node* uncle = grandFather->_right;
					if (uncle && RED == uncle->_color)
					{
						// 情况一:叔叔节点存在且为红
						parent->_color = BLACK;
						uncle->_color = BLACK;
						grandFather->_color = RED;
						cur = grandFather;
						parent = cur->_parent;
					}
					else
					{
						// 叔叔节点不存在 || 叔叔节点存在且为黑
						// 情况二和情况三
						if (cur == parent->_right)
						{
							// 情况三
							RotateLeft(parent);
							std::swap(cur, parent);
						}

						// 情况二
						parent->_color = BLACK;
						grandFather->_color = RED;
						RotateRight(grandFather);
					}
				}
				else
				{
					// 课件中三个场景的反情况:
					Node* uncle = grandFather->_left;
					if (uncle && RED == uncle->_color)
					{
						parent->_color = BLACK;
						uncle->_color = BLACK;
						grandFather->_color = RED;
						cur = grandFather;
						parent = cur->_parent;
					}
					else
					{
						if (cur == parent->_left)
						{
							RotateRight(parent);
							std::swap(cur, parent);
						}

						parent->_color = BLACK;
						grandFather->_color = RED;
						RotateLeft(grandFather);
					}
				}
			}
		}

		root->_color = BLACK;
		_head->_left = MostLeft();
		_head->_right = MostRight();
		++_size;
		return make_pair(iterator(newNode), true);
	}

	size_t Size()const
	{
		return _size;
	}

	bool Empty()const
	{
		return 0 == _size;
	}

	void Clear()
	{
		_DestroyRBTree(GetRoot());
		_head->_left = _head;
		_head->_right = _head;
		_size = 0;
	}

	void Swap(RBTree<T, KOFD, Compare>& t)
	{
		std::swap(_head, t._head);
		std::swap(_size, t._size);
		std::swap(_com, t._com);
	}

	void InOrder()
	{
		cout << "中序遍历: ";
		_InOrder(GetRoot());
		cout << endl;
	}

	iterator Find(const T& data)
	{
		Node* cur = GetRoot();
		KOFD kofD;
		while (cur)
		{
			if (_com(kofD(data), kofD(cur->_data)))
				cur = cur->_left;
			else if (_com(kofD(cur->_data), kofD(data)))
				cur = cur->_right;
			else
				return iterator(cur);
		}

		return end();
	}

	bool IsVAlidTree()
	{
		Node* root = GetRoot();
		if (nullptr == root)
			return true;

		if (root->_color == RED)
		{
			cout << "违反性质2:根是红色的" << endl;
			return false;
		}

		// 获取单条路径中黑色节点的个数
		size_t blackCount = 0;
		Node* cur = root;
		while (cur)
		{
			if (BLACK == cur->_color)
				blackCount++;

			cur = cur->_left;
		}

		// 递归检测性质4
		return IsVAlidTree(root, blackCount, 0);
	}

private:
	bool IsVAlidTree(Node* root, const size_t blackCount, size_t pathBlackCount)
	{
		if (nullptr == root)
			return true;

		Node* parent = root->_parent;
		if (parent != _head && 
			(RED == root->_color && RED == parent->_color))
		{
			cout << "违反了性质三:存在连在一起的红色节点!!!" << endl;
			return false;
		}

		if (BLACK == root->_color)
			pathBlackCount++;

		if (nullptr == root->_left && nullptr == root->_right)
		{
			if (pathBlackCount != blackCount)
			{
				cout << "违反了性质4:路径中黑色节点个数不一样!!!" << endl;
				return false;
			}
		}

		return IsVAlidTree(root->_left, blackCount, pathBlackCount) &&
			   IsVAlidTree(root->_right, blackCount, pathBlackCount);
	}
	void RotateLeft(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		parent->_right = subRL;
		// 注意:右单支
		if (subRL)
			subRL = subRL->_parent = parent;

		subR->_left = parent;

		// 更新subR和parent的双亲
		Node* pparent = parent->_parent;
		parent->_parent = subR;
		subR->_parent = pparent;

		// 需要向上考虑pparent
		if (_head == pparent)
			GetRoot() = subR;
		else
		{
			if (parent == pparent->_left)
				pparent->_left = subR;
			else
				pparent->_right = subR;
		}
	}

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

		parent->_left = subLR;
		// 左单支
		if (subLR)
			subLR->_parent = parent;

		subL->_right = parent;

		// 更新suL和parent的双亲
		Node* pparent = parent->_parent;
		subL->_parent = pparent;
		parent->_parent = subL;

		// 向上考虑pparent的情况
		if (_head == pparent)
			GetRoot() = subL;
		else
		{
			if (parent == pparent->_left)
				pparent->_left = subL;
			else
				pparent->_right = subL;
		}
	}

	void _DestroyRBTree(Node*& root)
	{
		if (root)
		{
			_DestroyRBTree(root->_left);
			_DestroyRBTree(root->_right);
			delete root;
			root = nullptr;
		}
	}
	void _InOrder(Node* root)
	{
		if (root)
		{
			_InOrder(root->_left);
			cout << root->_data << " ";
			_InOrder(root->_right);
		}
	}

	Node*& GetRoot()
	{
		return _head->_parent;
	}

	Node* MostLeft()
	{
		Node* root = GetRoot();
		if (nullptr == root)
			return _head;

		Node* cur = root;
		while (cur->_left)
		{
			cur = cur->_left;
		}

		return cur;
	}

	Node* MostRight()
	{
		Node* root = GetRoot();
		if (nullptr == root)
			return _head;

		Node* cur = root;
		while (cur->_right)
		{
			cur = cur->_right;
		}

		return cur;
	}


private:
	Node* _head;
	size_t _size;
	Compare _com;
};

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

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

相关文章

计算机组成原理(0)概述

前言 没有想好到底是要怎么学习&#xff0c;看哪个视频课&#xff0c;看到1.2 计算机的基本组成1.2-a1_哔哩哔哩_bilibili 是15年录的视频读PPT课本不一样就更不想看了&#xff0c;但是CSAPP难度大&#xff08;主要是广度&#xff0c;所以可能不很适用于考试或计组的学习&…

java代码审计和安全漏洞修复

java代码审计和安全漏洞修复 本文目录 java代码审计和安全漏洞修复开源&#xff1a;奇安信代码卫士官网使用gitee服务使用 非开源&#xff1a;思客云找八哥错误类型以及修改方法1.硬编码2. 路径操作3. 路径操作 - Zip文件条目覆盖4. SQL注入5. SQL注入 - Hibernate6. XML外部实…

视频与图片检索中的多模态语义匹配模型 ——原理、启示、应用与展望

前言 三多前笔者在《万字长文漫谈视频理解》[1]一文中&#xff0c;曾经将自己对视频理解的认识进行过简单总结&#xff0c;幸而获得了朋友们的认可&#xff0c;能让读者认可是笔者最为骄傲的成就。现在看来文中观点有不少纰漏狭隘之处&#xff0c;特别是近年来多模态模型的流行…

6.5this关键字

1. 关键字&#xff1a;this 1.1 this 是什么&#xff1f; 首先。this在Java中是一个关键字&#xff0c;this 指代的是本类的引用对象 1.2 什么时候使用 this 1.2.1 实例方法或构造器中使用当前对象的成员 1、在实例方法或构造器中&#xff0c;我们在使用get和set方法中使用…

行为型设计模式06-迭代器模式

&#x1f9d1;‍&#x1f4bb;作者&#xff1a;猫十二懿 ❤️‍&#x1f525;账号&#xff1a;CSDN 、掘金 、个人博客 、Github &#x1f389;公众号&#xff1a;猫十二懿 迭代器模式 1、迭代器模式介绍 迭代器模式是一种行为型设计模式&#xff0c;它提供了一种方法来访问聚…

HydroD 实用教程(九)时域水动力分析

目 录 一、前言二、前处理三、定义/提交作业3.1 创建分析作业3.2 定义输入数据3.3 设置执行指令3.4 指定输出格式3.5 提交求解计算 四、输出文件五、结果后处理5.1 绘制力/位移时程5.2 傅里叶变换与导荷5.3 播放时域结果动画 六、参考文献 一、前言 SESAM &#xff08;Super El…

扫描仪连续扫描提示有一个问题阻值扫描该文档。请重试,错误的解决办法

故障现象: 用户新安装的联想M7650DNA一体多功能激光打印机,安装完所有驱动后;打印、复印都正常,只有扫描不正常,扫描多张后就会提示:有一个问题阻值扫描该文档。请重试,或者参阅“帮助和支持”或扫描仪附带的信息,了解有关疑难解答的信息。如下图:故障。 开始怀…

基于Pytest+Allure+Excel的接口自动化测试框架

1. Allure 简介 简介 Allure 框架是一个灵活的、轻量级的、支持多语言的测试报告工具&#xff0c;它不仅以 Web 的方式展示了简介的测试结果&#xff0c;而且允许参与开发过程的每个人可以从日常执行的测试中&#xff0c;最大限度地提取有用信息。 Allure 是由 Java 语言开发…

Maven项目管理-随笔(入门)

目录 前言 什么是Maven Maven的优点 Maven的核心概念有哪些 POM是什么 什么是依赖管理 什么是插件 什么是仓库 概述 1、构建 2、依赖 安装与配置 1、下载 2、Windows Maven安装 1&#xff09;解压到指定目录 2&#xff09;配置环境变量 3&#xff09;目录结构 …

离散数学题目收集整理练习(期末过关进度60%)

✨博主&#xff1a;命运之光 &#x1f984;专栏&#xff1a;离散数学考前复习&#xff08;知识点题&#xff09; &#x1f353;专栏&#xff1a;概率论期末速成&#xff08;一套卷&#xff09; &#x1f433;专栏&#xff1a;数字电路考前复习 ✨博主的其他文章&#xff1a;点击…

基于ssm框架的数字化题库与在线考试系统设计与实现+第二稿+文档

博主介绍&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 项目名称 基于ssm框架的数字化题库与在线考试系统设计与实现第二稿文档 视频演示 视频去哪了呢&#xff1f;_哔哩哔哩_bilibili 系统介绍 摘 要 随着科学技术…

iOS游戏反外挂方案解析

自2007年iPhone OS发布以来&#xff0c;iOS系统已经发展了近17年&#xff0c;凭借着独家的系统环境、安全性更高的闭源生态等优势。iOS从一众手机系统中脱颖而出&#xff0c;与安卓稳坐手机系统市场两把头部交椅。 不同于安卓的开源生态&#xff0c;iOS的闭源生态中的硬件、软…

【计算机网络自顶向下】如何学好计网-第五章数据链路层

第五章 数据链路层 学习目的 目的1&#xff1a;理解链路层服务的主要功能 差错检查、纠错 共享广播信道&#xff1a;多点接入问题(multiple access) 链路层寻址(link layer addressing) 局域网技术&#xff1a;Ethernet, VLANs 目的2&#xff1a;链路层技术的实现 点到点…

Vue介绍与入门(一)

文章目录 前言一、Vue.js是什么&#xff1f;二、vue入门1. 引入vue.js2. 编写入门的简易代码&#xff08;实践&#xff09; 三、vue学习总结&#xff08;重点&#xff09; 前言 前端开发三大框架 1、Vue&#xff1a;尤雨溪主导开发 2、React&#xff1a;脸书&#xff08;Faceb…

MySQL 02:常用数据类型

<~生~信~交~流~与~合~作~请~关~注~公~众~号生信探索> 主要的数据类型&#xff0c;包括字符串、数值、日期时间 数值型 INT就是整数类型&#xff0c;根据允许的数值大小分为以下类型&#xff08;由小到大&#xff09;&#xff0c;这样做的目的是节约空间 INT类型范围&…

【Vscode 远程连接 Docker 容器】

文章目录 1. 配置docker镜像2. 安装 OpenSSH3. Vscode中安装 Remote-SSH 插件&#xff1a;4. 配置连接信息 1. 配置docker镜像 在主机目录下创建一个 Dockerfile&#xff0c;注意文件名必须保持一致&#xff01;&#xff01;&#xff01;&#xff08;默认装了docker&#xff09…

从零开始了解Redis 主从复制全部流程

主从复制 主从复制介绍 分析单个Redis 的问题 在一个项目中读的操作是比写的操作要多的 像京东&#xff0c;淘宝等等同一时刻看的人是远远多于买的人的所有单个redis既要承担写的操作又要承担读的操作效率低在高并发的情况下不稳定 所以引出了主从复制 一图胜千言 Redis …

数据库入门下篇(如何安装和登录MYSQL数据库)

在这篇文章里&#xff0c;笔者将着重讲解如何在win和Linux系统上安装自己的MySQL数据库软件&#xff0c;以及安装好数据库软件后如何启动和登录&#xff0c;忘了密码怎么办&#xff1f;如何创建一个数据库&#xff0c;如何在数据库中创建一个表等内容 目录 在windows系统上安装…

宠物行业 | 活动落地页设计指南基础版

中国是全球第二大宠物市场&#xff0c;同时也是增长最快的市场之一。随着养宠人群的扩大&#xff0c;人宠亲情关系的加深&#xff0c;客群消费意愿与消费水平的提高&#xff0c;中国宠物行业正处于消费与认知的全面升级期。 调研显示&#xff0c;2022年我国宠物产业规模达4936亿…

管理类联考——英语二——技巧篇——写作——图表作文——经典方法论

考研英语(二)的B节写作主要考查的是图表作文。笔者根据考研英源(二)大纲要求以及议论文经典的三段式写法(首段指出问题、中间段分析问题、尾段解决问题)&#xff0c;研发出一套图表作文的经典写法。下面我们来看图表作文经典的三段式写法的基本大招。 从上图可以看出&#xf…