手撕红黑树——C++高阶数据结构详解

传统艺能😎

小编是双非本科大一菜鸟不赘述,欢迎各位指点江山(期待~)(QQ:1319365055)
此前博客点我!点我!请搜索博主 【知晓天空之蓝】

🎉🎉非科班转码社区诚邀您入驻🎉🎉
小伙伴们,打码路上一路向北,彼岸之前皆是疾苦
一个人的单打独斗不如一群人的砥砺前行
这是和梦想合伙人组建的社区,诚邀各位有志之士的加入!!
社区用户好文均加精(“标兵”文章字数2000+加精,“达人”文章字数1500+加精)
直达: 社区链接点我


在这里插入图片描述


红黑树是对树结构的一种高度综合运用,涉及到多叉树,树平衡调整,节点旋转等等,这些是对数据结构基本功的最佳历练,也是面试场上令人闻风丧胆的冷面一刀。能否给出完整的定义,能否介绍自己对红黑树的认识,能否通过旋转,染色等操作在给定的场景下对一颗红黑树进行调整使其符合定义…这些才是我们应该在学习后得到的信息

概念🤔

红黑树也是一种二叉搜索树,但是每个节点都存储了一个颜色,该颜色可以为黑可以为红,因此也叫红黑树。

红黑树和 AVL 树的区别就是红黑树属于近似平衡,他并不是完全平衡,红黑树不会像 AVL 树一样做到严密的平衡(高度差控制在 1 ),他是依靠每条路径上红黑节点的排布与限制,确保最长路径长度不超过最短路径长度的 2 倍.

在这里插入图片描述

五大特性🤔

红黑树有着标志性的五大特性

  1. 根节点一定为黑色
  2. 一个节点只能是红或黑
  3. 节点为红色,则该节点的两个子节点都为黑色
  4. 对于每个节点,从根节点到叶子结点的简单路径上,黑色节点个数相等
  5. 每个叶子结点(即空节点)都是黑色

那么问题来了,仅仅依靠这五大特性是如何确保最长路径长度 < 最短路径长度的 2 倍?

根据第 3,4 特性,我们不妨思考一下极端场景,最短可能路径其实就是全黑的情况,假设此时有 n 个节点,长度就为 n:

在这里插入图片描述

而最长可能路径就是在红色节点存在的条件下,以一红一黑的方式进行排列,此时假设依然有 n 个黑色节点,那么红色节点数目与黑色相同,则长度为 2n,以此证明。

节点定义🤔

这里和 AVL 树一样我们直接实现 KV 模型,为了方便后序的旋转操作,将红黑树的结点定义为三叉链结构,将 AVL 树中的平衡因子换成了新成员——结点的颜色。

因为节点颜色是贯穿的,我们可以直接定义成全局变量,并用枚举变量来给颜色赋予逻辑:

enum Color 
{
   
   Red, //0
   Black  //1
};

接下来就可以定义节点了:

template<class K,class V>
struct RBTreeNode
{
   
	RBTreeNode(const pair<K, V>& kv, Color color = Red)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _color(color)
	{
   }

	RBTreeNode<K,V>* _left;
	RBTreeNode<K,V>* _right;
	RBTreeNode<K,V>* _parent;//三叉链结构
	pair<K,V> _kv;//存储键值对
	Color _color;//节点颜色
};

注意这里有个细节:为什么构造结点时,默认将结点的颜色设置为红色?

因为我们要考虑插入场景,不论此时根节点为黑色还是红色,我们插入黑色节点,那么他一定会破坏性质 4 ,某条路径上的黑色节点一定会增加一个,那么为了维护结构,我们岂不是要给其他所有路径都加上一个黑色节点?但如果此时插入的是红色节点,如果根节点为红色,那么又会破坏性质 3 出现了连续的红色节点,但是如果根节点为黑色,就不需要进行调整。

这就是一个简单的伤害最小化理论:插入黑节点,一定会破坏性质 4,必须进行调整;插入红节点,可能破坏性质 3,可能进行调整,因此我们选择将插入节点定为红色。

红黑树插入🤔

插入逻辑依然和二叉搜索树差不多,三步走:

  1. 按二叉搜索树的插入方法,找到待插入位置。
  2. 将待插入结点插入到树中。
  3. 若插入结点的父结点是红色的,则需要对红黑树进行调整。

插入的关键就在于这里的第三步,这调整里面有大学问。

插入调整🤔

我们给出一个基本模型(可以是一个完整的树,也可以是一个子树):

在这里插入图片描述

并约定:

cur :当前节点
p:parent,父节点
g:grandfather,祖父节点
u:uncle,叔叔节点
a,b,c,d,e:子树

顺便提一下,树的路径不是一路走到叶子结点算一条,要走到空节点(NIL 节点)算一条:

在这里插入图片描述

比如上图中该红黑树的有效路径不是 4 条而是 9 条。

情况一😎

cur 为红,p 为红,g 为黑, u 存在且为红

在这里插入图片描述

首先你需要知道,红黑树的调整关键看叔叔,因为节点的颜色是固定的,祖父存在且一定为黑,为什么?因为父节点为红色那么他一定不会是根,他一定有一个父节点且为黑,只有 u 节点不知道嘛情况。

我们调整并不是像 AVL 一样旋转,这里是进行变色,首先将叔叔变黑,此时就没有连续的红节点了,但是为了保持黑节点数目不变父亲也要变黑,万事大吉了吗?没有,紧接还要将祖父变红,为什么呀!?不要忘了,这里可能只是一个子树,叔叔父亲变黑了那么路径上黑节点就会多出一个,对整个结构必然有影响,所以还需要将祖父变红保持黑色数量不变。

在这里插入图片描述
最后不要忘了将祖父当成 cur 节点继续向上调整,因为在上一层父亲的颜色不知道,祖父改变可能违反规则还会继续调整。这种情况我们左右方向,翻转过来处理方法一样,只变色不旋转。

具体代码表示:

			while (parent != _pHead && parent->_color == Red)
			{
   
				Node* grandfather = parent->_parent;
				assert(grandfather);   //祖父节点有效性
				if (parent == grandfather->_left)
				{
   
					Node* uncle = grandfather->_right; //情况一叔叔在祖父右
					if (uncle && uncle->_color == Red) //叔叔在右且为红
					{
   
						parent->_color = Black;
						uncle->_color = Black; 
						grandfather->_color = Red; 
						cur = grandfather; //继续向上调整
						parent = cur->_parent;
					
  • 135
    点赞
  • 132
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 33
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 33
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乔乔家的龙龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值