C++进阶 —— 红黑树

红黑树(Red Black Tree,RBTree) 一、概念 红黑树是一种自平衡二叉查找树(二叉排序树)。与平衡二叉树(avl树)不同的是,红黑树是弱平衡二叉树,即它的左右子树高度差有可能大于1。 二、5大性质 每个节点要么是黑色, 要么是红色。 根节点是黑色。 每个叶节点(Nil,空值)都是黑色。 每个红色节点的两个子节点一定都是黑色。 任意一个节点到每个叶子节点的路径都包含相同数量的黑节点。 三、推论(或规律) 左子树和右子树的黑节点的层数是相等的。因此我们称红黑树的这种平衡为黑色完美平衡。(由性质5推出) 如果一个节点存在黑子节点,那么该节点肯定 阅读详情

目录

一,红黑树的介绍

红黑树的性质

红黑树的结构

二,红黑树的插入

三,红黑树的验证及删除

红黑树的验证

红黑树的删除

红黑树与AVL树的比较

四,红黑树的应用

红黑树的应用

红黑树模拟实现set/map


一,红黑树的介绍

  • 红黑树是一种二叉搜索树,但在每个节点上增加一个存储位(表示节点颜色),可以是red或black;
  • 通过任何一条从根到叶子的路径上各个节点的着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的;
  • 保证了在最坏情况下,查找、插入、删除的时间复杂度都为 O(log n);

红黑树的性质

  • 每个节点不是红色,就是黑色;
  • 根节点是黑色的;
  • 如一个节点是红色的,则它的两个孩子节点必须是黑色的;
  • 对于每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点;
  • 每个叶节点都是黑色的(此处的叶节点指的是空节点);
//红黑树节点的定义
enum Color { RED, BLACK };

template<class ValueType>
struct RBTreeNode
{
	RBTreeNode(const ValueType& data = ValueType(), Color color=RED)
		:_pLeft(nullptr)
		,_pRight(nullptr)
		,_pParent(nullptr)
		,_data(data)
		,_color(color)
	{}

	RBTreeNode<ValueType>* _pLeft;
	RBTreeNode<ValueType>* _pRight;
	RBTreeNode<ValueType>* _pParent;
	ValueType _data;
	Color _color;
};

红黑树的结构

        为了后续实现关联式容器简单,红黑树的实现中增加一个头节点;因为根节点必须为黑色,为了与根节点进行区分,将头节点给成黑色,并让头节点的_pParent指向红黑树的根节点,_pLeft指向红黑树中最小的节点,_pRight指向红黑树中最大的节点;

二,红黑树的插入

红黑树是在二叉搜索树的基础上加上其平衡限制条件,红黑树的插入可分为两步:

1,按照二叉搜索树的规则插入新节点;

  • 像普通二叉搜索树一样,插入新节点,并着色为红色;
template<class ValueType>
class RBTree
{
	bool Insert(const ValueType& data){
		pNode& pRoot = GetRoot();
		if (pRoot == nullptr){
			pRoot = new Node(data, BLACK);
			pRoot->_pParent = _pHead;
			_pHead->_pParent = pRoot;
		}
		else{
			pRoot->_color = BLACK;
			_pHead->_pLeft = LeftMost();
			_pHead->_pRight = RightMost();
			return true;
		}
	}
private:
	pNode& GetRoot() { return _pHead->_pParent; }
	pNode LeftMost();
	pNode RightMost();
private:
	pNode _pHead;
};

2,检测新节点插入后,红黑树的性质是否遭到破坏;

  • 如新插入的节点,其双亲节点颜色是黑色,没有违反红黑树任何性质,则不需要调整;
  • 如新插入的节点,其双亲节点颜色为红色时,就违反了性质,此时就需要调整,需分以下情况来分析(约定当前节点为cur,父节点为p,祖父节点为g,叔叔节点为u);

情况一:cur为红,p为红,g为黑,u为红;

解决方法:将p、u改为黑色,g改为红,然后把g当成cur,继续向上调整;

  • 如g是根节点,调整完后,需将g改为黑色;
  • 如g是子树,g一定有双亲,如该双亲也是红色,需继续向上调整;

情况二:cur为红,p为红,g为黑,u为黑或不存在,直线型 (Left-Left or Right-Right);

  • Left-Left:cur是p的左孩子,p是g的左孩子;
  • Right-Right:cur是p的右孩子,p是g的右孩子;

解决方法:将p改为黑色,g改为红,然后对g进行一次右旋(L-L)或进行一次左旋(R-R);

  • p为g左孩子,cur为p的左孩子,则进行右单旋;
  • p为g右孩子,cur为p的右孩子,则进行左单旋;
  • p变黑,g变红;

情况三:cur为红,p为红,g为黑,u为黑或不存在,折线型 (Left-Right or Right-Left);

  • Left-Right:cur是p的右孩子,p是g的左孩子;
  • Right-Left:cur是p的左孩子,p是g的右孩子;

解决方法:

  • p为g的左孩子,cur为p的右孩子,则针对p左单旋;
  • p为g的右孩子,cur为p的左孩子,则针对p右单旋;
  • 转化为情况2;

三,红黑树的验证及删除

红黑树的验证

  • 检测其是否满足二叉搜索树(中序遍历是否为有序序列);
  • 检测其是否满足红黑树的性质;
bool IsValidRBTree()
{
	pNode pRoot = GetRoot();
	if (pRoot == nullptr)
		return true;
	if (pRoot->_color == BLACK)
	{
		cout << "违反红黑树根节点必须为黑色" << endl;
		return false;
	}
	size_t blackCount = 0;
	pNode pCur = pRoot;
	while (pCur)
	{
		if (pCur->_color == BLACK)
			blackCount++;
		pCur = pCur->_pLeft;
	}

	size_t k = 0;
	return _IsValidRBTree(pRoot, k, blackCount);
}

bool _IsValidRBTree(pNode pRoot, size_t k, const size_t blackCount)
{
	if (pRoot == nullptr)
	{
		if (blackCount != k)
		{
			cout << "违反性质每条路径中黑色节点个数必须相同" << endl;
			return false;
		}
		return true;
	}

	if (pRoot_color == BLACK)
		k++;

	pNode pParent = pRoot->_pParent;
	if (pParent && pParent->_color == RED && pRoot->_color == RED)
	{
		cout << "违反性质没有连在一起的红色节点" << endl;
		return false;
	}

	return _IsValidRBTree(pRoot->_pLeft, k, const blackCount) &&
		   _IsValidRBTree(pRoot->_pRight, k, const blackCount)
}

红黑树的删除

可参考《算法导论》或《STL源码剖析》;

或参考博客,红黑树 - _Never_ - 博客园;

红黑树与AVL树的比较

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

四,红黑树的应用

  • C++ STL库
    • set/multiset、map/multimap
  • Java库
  • Linux内核
  • 其他一些库

红黑树模拟实现set/map

红黑树的迭代器(迭代器的好处是方便迭代)

  • begin()/end(),STL规定begin()/end()是一段前闭后开的区间,而对于红黑树的遍历得到是有序序列,因此begin()可放在红黑树中最小节点(即最左侧节点)的位置,end()放在最大节点(即最右侧节点)的下一个位置;下一个位置不可给成nullptr,因为对迭代器--操作时需找到最后一个元素,所以最好方式是将end()放在头节点的位置;

  •  operator++()或operator--()
void Increasement()
{
	if (_pNode->_pRight) //当前节点有右子树,下一个是右子树的最小值
	{
		_pNode = _pNode->_pRight;
		while (_pNode->_pLeft) {
			_pNode = _pNode->_pLeft;
		}
	}
	else //如没有右子树,向上回溯
	{
		pNode pParent = _pNode->_pParent;
		while (pParent->_pRight == _pNode) //当前节点是父节点的右孩子
		{
			_pNode = pParent;
			pParent = _pNode->_pParent;
		}

		if(_pNode->_pRight != pParent) //边界检查
			_pNode = pParent;
	}
}
void Decreasement()
{
	//_pNode在head的位置,当迭代器在 end() 位置时,它指向header节点;
	if (_pNode->_pParent->_pParent == _pNode && _pNode->_color == RED)
		_pNode = _pNode->_pRight;

	else if(_pNode->_pLeft) //当前节点有左子树,下一个是左子树的最大值 
	{
		_pNode = _pNode->_pLeft;
		while (_pNode->_pRight) {
			_pNode = _pNode->_pRight;
		}
	}
	else //如没有左子树,向上回溯
	{
		pNode pParent = _pNode->_pParent;
		while (_pNode == pParent->_pLeft) {
			_pNode = pParent;
			pParent = _pNode->_pParent;
		}
		_pNode = pParent;
	}
}

改造红黑树

//因关联式容器中存储的是键值对<key, value>
//K为key的类型
//ValueType, 如是map,则为pair<K, V>,如是set,则为K
//KeyofValue,通过value来获得key的一个仿函数类
template <class K, class ValueType, class KeyofValue>
class RBTree
{
	typedef RBTreeNode<ValueType> Node;
	typedef Node* pNode;
public:
	typedef RBTreeIterator<ValueType, ValueType*, ValueType&> Iterator;
public:
	RBTree();
	~RBTree();

	Iterator begin() { return Iterator(_pHead->_pLeft); }
	Iterator end() { return Iterator(_pHead); }

	pair<Iterator, bool> Insert(const ValueType& data)
	{
		//插入节点并调整
		return make_pair(Iterator(pNewNode), true);
	}

	void clear();
	Iterator find(const k& key);

	size_t size() const;
	size_t empty() const;

private:
	pNode _pHead;
	size_t _size;
};

set的模拟实现

  • set的底层结构就是红黑树,因此在set中直接封装了一颗红黑树;
template<class K>
class set
{
	typedef K ValueType;
	struct KeyOfValue {
		const K& operator()(const ValueType& key) {
			return key;
		}
	};

	typedef RBTree<K, ValueType, KeyOfValue> RBTree;
public:
	typedef typename RBTree::Iterator iterator;
public:
	set() {};

	iterator begin() { return _t.begin() };
	iterator end() { return _t.end() };

	size_t size() const { return _t.size() };
	bool empty() const { return _t.empty() };
	void clear() { return _t.clear() };

	pair<iterator, bool> insert(const ValueType& data) {
		return _t.Insert(data);
	}

	iterator find(const K& key);
private:
	RBTree _t;
}

map的模拟实现

  • map的底层结构就是红黑树,因此在map中直接封装了一颗红黑树;
template<class K, class V>
class map
{
	typedef pair(K, V) ValueType;
	struct KeyOfValue {
		const K& operator()(const ValueType& v) {
			return v.first;
		}
	};

	typedef RBTree<K, ValueType, KeyOfValue> RBTree;
public:
	typedef typename RBTree::Iterator iterator;
public:
	map() {};

	iterator begin() { return _t.begin() };
	iterator end() { return _t.end() };

	size_t size() const { return _t.size() };
	bool empty() const { return _t.empty() };
	void clear() { return _t.clear() };

	V& operator[](const K& key) {
		return (*(_t.Insert(ValueType(key, V())))).first).second;
	}
	const V& operator[](const K& key)const;

	pair<iterator, bool> insert(const ValueType& data) {
		return _t.Insert(data);
	}

	iterator find(const K& key) {
		return _t.find(key);
	}
private:
	RBTree _t;
}


 

【C++详解】——红黑树 本文介绍了红黑树的概念、性质,详细解释了红黑树插入节点的过程。 阅读详情

相关推荐

红黑树简介及左旋、右旋、变色

红黑树简介及红黑树的左旋、右旋、变色

weixin_43790276的博客 1万+

【数据结构】史上最好理解的红黑树讲解,让你彻底搞懂红黑树

狂肝半个月的学习笔记,最通俗易懂的红黑树讲解。带你快速掌握红黑树~

小七的博客 29万+

WSL子系统启动报错 Wsl/Service/CreateInstance/CreateVm/HCS_E_SERVICE_NOT_AVAILABLE

WSL子系统启动报错 :Wsl/Service/CreateInstance/CreateVm/HCS_E_SERVICE_NOT_AVAILABLE 于是分析原因,最终发现是Windows功能中的虚拟机平台功能不知道什么时候被取消了。

Linux I Tell U 6万+

红黑树介绍

红黑树一、红黑树的概念红黑树的性质红黑树节点的定义红黑树结构二、使用步骤1.引入库2.读入数据总结 一、红黑树的概念 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。 红黑树的性质 每个结点不是红色就是黑色 根节点是黑色的 如果一个节点是红色的,则它的两个孩子结点是黑色的 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数

mz474920631的博客 4万+

LIGGGHTS颗粒模拟入门:从DEM原理到工程实践

离散元法(DEM)是一种用于模拟颗粒物质行为的数值方法,其核心原理是通过计算每个颗粒的运动和相互作用来预测颗粒系统的宏观行为。该方法基于牛顿第二定律和接触力学模型,能够精确描述颗粒间的碰撞、摩擦和粘结等微观机制。在工程实践中,DEM技术为优化工业设备设计、预测材料流动和减少实验成本提供了重要价值。它广泛应用于制药造粒、矿石破碎、粉体输送和农业加工等领域,帮助工程师和研究人员深入理解颗粒系统的复杂动力学。LIGGGHTS作为一款基于LAMMPS的开源DEM软件,继承了其高效的并行计算架构,并针对颗粒模拟进行了

weixin_33979203的博客 418

双有源桥变换器(DAB)TPS控制:从理论建模到Simulink/PSIM仿真实践

本文深入探讨了双有源桥变换器(DAB)的三重移相(TPS)控制技术,从理论建模到仿真实现。文章详细分析了TPS相比SPS和DPS的优势,阐述了其12种工况的划分逻辑与电流应力最优调制策略,并提供了在Simulink和PSIM平台搭建仿真模型的详细步骤与实战经验,帮助工程师掌握这一高效控制方法。

white的博客 584

《C++进阶之STL》【红黑树】

本文介绍了红黑树的基本概念、特性和操作并分步骤详细的实现了红黑树,以及使用代码测试比较了红黑树和AVL树在性能的差距。文章还分析了红黑树如何通过最长路径不超过最短路径两倍的特性保证平衡,并详细说明了插入操作中的变色、旋转等调整策略。

序属秋秋秋的博客 2033

基于springboot医院医疗仪器管理系统

基于Spring Boot的医院医疗仪器管理系统是一款专门为医院设计的高效管理工具。

2201_75927219的博客 1012

【C++进阶】红黑树

红黑树(Red-Black Tree)是一种自平衡的二叉搜索树,用于保持树的平衡,以确保在最坏情况下基本操作(如插入、删除和查找)的时间复杂度仍为 O(log n)。红黑树的每个节点都包含一个额外的颜色位,即红色或黑色。红黑树通过严格的平衡条件来维持树的平衡。

2301_79969994的博客 1748

UE 顶点动画工具 (VAT)

导入模型需要勾选全精度UV。

iesoxj的博客 1640

爆肝5万字❤️Open3D 点云数据处理基础(Python版)

本文为Open3D 点云数据处理基础教程(Python版),小白也能轻松上手!还等什么呢,学起来!

孙悟空 13万+

【C++进阶06】红黑树图文详解及C++模拟实现红黑树

红黑树详解、模拟实现

2302_76421042的博客 4196

包成功安装tiny-cuda-nn,记录安装过程中的问题解决,附带pytorch3d安装【踩坑指南】

该包可以显著提高NeRF训练速度,是和NeRFstudio等框架中,必须使用的。本文提供tiny-cuda-nn可以安装的完整包的下载链接,可以直接上传到项目中安装。附带笔者遇到的问题的解决方案。

zik的博客 7029

【C++进阶】封装红黑树实现map和set

【C++进阶】封装红黑树实现map和set

2301_78022459的博客 1041

锂电池SOH估计 | 基于LSTM模型的锂电池SOH估计(Python)

锂电池SOH估计 | 基于LSTM模型的锂电池SOH估计(Python)

前程算法屋的博客 1433

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

C++进阶——红黑树

enum color //定义颜色 {color _col;} };思考:在节点的定义中,为什么要将节点的默认颜色给成红色的?因为刚刚说的性质中第三条比第四条更好维护。

weixin_43635473的博客 440

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

【C++进阶】第7课—红黑树

【代码】【C++进阶】第7课—红黑树。

weixin_70620792的博客 1070

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

c++进阶--红黑树

大家好,今天我们继续来学习c++的进阶部分,今天我们要学习的内容是红黑树。1. 红⿊树的概念红⿊树是⼀棵⼆叉搜索树,他的每个结点增加⼀个存储位来表⽰结点的颜⾊,可以是红⾊或者⿊⾊。通过对任何⼀条从根到叶⼦的路径上各个结点的颜⾊进⾏约束,红⿊树确保没有⼀条路径会⽐其他路径⻓出2倍,因⽽是接近平衡的。1.1 红⿊树的规则:1. 每个结点不是红⾊就是⿊⾊2. 根结点是⿊⾊的3. 如果⼀个结点是红⾊的,则它的两个孩⼦结点必须是⿊⾊的,也就是说任意⼀条路径不会有连续的红⾊结点。

2302_79606537的博客 1038

C++进阶—【红黑树】

这里为什么节点的颜色默认是红色的?因为节点如果默认是黑色,那么只要新增节点就一定会破坏红黑树的第四个条件(每个路径上的黑色节点相同),而节点默认是红色,则有概率插入到正确的位置,即使出现连续的红色节点,我们也可以通过调整节点的颜色来使红黑树恢复正常。

一个努力拼搏的人 734

C++进阶——红黑树的实现

红黑树是一种自平衡的二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是红色或黑色。通过对从根节点到叶子节点的路径上的节点颜色进行约束,红黑树确保没有一条路径会比其他路径长出两倍,因此它是近似平衡的。

博客 | 复习知识点 + 练习 + 最终的领悟 | 全力以赴 966

C++进阶:红黑树

/节点的颜色enumColorREDBLACK//红黑树节点的定义templateclassValueTypestructRBTreeNodeRBTreeNodeconstValueTypedataValueType()RED_pLeftnullptr),_pRightnullptr),_pParentnullptr_datadata),_colorcolor{}RBTreeNodeValueType>*_pLeft;//

qq_71925940的博客 853

★ C++进阶篇 ★ 红黑树实现

★ C++进阶篇 ★ 红黑树实现

2302_80328146的博客 1661

c++进阶——红黑树的实现

c++进阶——红黑树的实现

2301_79705195的博客 859
上一篇: C++进阶 —— AVL树
下一篇: C++进阶 —— 哈希
挨代码
博客等级 码龄5年 1696粉丝 384原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值