[C++]——红黑树(附源码)

 

目录

 

一、前言

二、正文

2.1 红黑树的概念

2.2 红黑树的性质

2.3红黑树节点的定义

 2.4 红黑树的插入

 2.4.1 情况一

2.4.2 情况二 

​编辑 2.4.3  情况三 

 2.5 红黑树的验证

 三、全部代码

 四、结语


一、前言

        在上一篇博客中,为小伙伴们进行了AVL树的讲解,但是由于在实际场景中大部分插入的数据都是无序的,所以红黑树的应用相较于AVL树会更会广泛,插入相同的数据,红黑树的旋转次数会比AVL树少很多,且其层数也不会像二叉搜索树那样高。因此在本篇博客中为大家带来红黑树的讲解,如有不足之处,欢迎各位大佬们给予指正!

二、正文

2.1 红黑树的概念

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

        以下图为例我们发现最短的路径上有三个节点,最长的路径有4个节点,并没有超出最短路径的两倍。

2.2 红黑树的性质

        在上面中我们了解到一颗红黑树中,最长路径的节点不超过最短路径的两倍,那么我们到底该如何让一颗二叉搜索树变成这样的一颗红黑树呢?这就需要满足红黑树的以下几个性质:

        1. 每个结点不是红色就是黑色

        2. 根节点是黑色的 

        3. 如果一个节点是红色的,则它的两个孩子结点是黑色的 

        4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点 

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

2.3红黑树节点的定义

        那么在了解完红黑树的概念及其性质后,接下来我们就来讲讲该如何实现这样的一颗红黑树。

        首先我们要明确的是红黑树的底层结构其实是与AVL树是一样的,都是一颗搜索二叉树,且为了使他们的结构都能达到各自的平衡,因此都需要采取三叉链的结构,即树节点内必须包含父亲,左孩子和右孩子三个节点指针,以方便树的旋转平衡。除此之外,既然是红黑树,其节点就是有“颜色”的,那么理所应当的,我们在每个节点内还需要定义一个颜色,我们可以通过枚举的方式来实现,这也是其与AVL树不同的地方所在。

        当然这里还有一个额外需要注意的地方,就是对于一个节点的初始化,其三叉链的节点指针好说,定义为空指针即可,但是颜色我们该定义为黑色还是红色呢?在这里我们采取的是红色,这里其实是考虑在尽量影响较少红黑树的结构下我们选择的,因为如果当我们将新插入节点的颜色定义为黑色时,那么该节点所处路径下的黑色节点数就都会加一,就会与其他路径的黑色节点数不同,在调整次数为各个路径黑色节点数相同就会比较麻烦;反之若是插入新节点的颜色为红色,那么就不会影响该路径的黑色节点数,倘若插入处的父亲节点为黑色,那么插入后仍为一颗红黑树。如果父亲节点为红色,虽然违背了红黑树“红色节点的左右孩子节点都是黑色”这一点,但是通过变色或者旋转的方式我们还是能够比较轻松将其重新变成一颗红黑树相较于去调整其他路径的黑色节点数来说,因此我们在创建一个新节点的时候,会将其颜色初始化为红色。

        具体代码如下:

//枚举:红色和黑色
enum Color
{
	RED,
	BLACK
};

//红黑树节点的定义
template <class K,class V>
struct RBTreeNode
{
	//采取三叉链的方式方便树的旋转平衡
	RBTreeNode<K, V>* _left;
	RBTreeNode<K,  V>* _right;
	RBTreeNode<K, V>* _parent;
    //颜色
	Color _col;
	pair<K, V> _kv;

	RBTreeNode(const pair<K ,V> kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(RED)    //默认初始化为红色
		, _kv(kv)
	{}
};

 2.4 红黑树的插入

        在成功创建完一个红色的新节点后,接下来的操作就是将这个新节点插入树中。

        首先,第一步就是找到其应所处的位置。由于红黑树的底层结构是一颗二叉搜索树,因此我们只需要从根节点开始不断进行数值的比较,若是比插入节点的数值大就来到左子树;比插入的节点数据小,就来到右子树,直至找到空指针为止,那么该处就是新节点所应该插入的位置。要注意的是如果遇到插入的数据在比较时遇到相同时就应该退出来,无需插入。(无重复数据的红黑树)

        第二步就是进行新节点与父亲节点的链接,若是父亲节点的数据大于新节点,就链接到其左子树,小于新节点,则链接到右子树。

        第三步就是进行红黑树规则的判断,判断在插入该节点后是否仍满足一颗红黑树,若是不满足则需要进行相对应的调整,使其重新变为一颗红黑树,这也是其在插入过程中最为重要的一步。那么我们该如何判断在插入新节点后,此树是否仍为一颗红黑树呢,其实在上面新节点的颜色选择上已经点出了,由于我们插入新节点的颜色是红色,那么其违反的规则只有一条,就是“红色节点的左右子树必须都为黑色节点”,因此只要新节点的父亲节点为红色,就说明我们插入新节点后,此树就不再是一颗红黑树,需要我们对此树进行一个调整使其重新变成一颗红黑树。

        代码框架如下:


                
### 红黑树数据结构 红黑树是一种自平衡二叉搜索树,其节点具有额外的颜色属性(红色或黑色),并通过一系列规则来保持树的近似平衡状态。以下是红黑树的核心定义及其数据结构: #### 节点定义 红黑树中的每个节点通常由以下几个部分组成[^1]: - `color`: 表示节点的颜色,可以是红色 (`1`) 或黑色 (`0`)。 - `data`: 存储在该节点中的实际数据。 - `parent`: 指向父节点的指针,用于辅助旋转和其他调整操作。 - `lchild` 和 `rchild`: 分别指向左子节点和右子节点。 ```c typedef char DataType; typedef struct RBNode { int color; // 结点颜色(0: 黑色, 1: 红色) DataType data; // 结点存储的数据 struct RBNode *parent; // 父结点指针 struct RBNode *lchild; // 左孩子指针 struct RBNode *rchild; // 右孩子指针 } RBNode, *RBTree; ``` --- ### 红黑树的算法实现 #### 插入操作 红黑树的插入操作分为两个阶段:普通二叉搜索树的插入以及后续的修复过程。具体如下: ##### 阶段一:普通二叉搜索树插入 按照标准二叉搜索树的方式将新节点插入到合适的位置,并将其初始化为红色节点[^2]。 ##### 阶段二:修复违反红黑树性质的情况 为了满足红黑树的特性,在插入完成后可能需要执行以下三种主要的操作之一: 1. **变色**: 如果当前节点与其父节点均为红色,则通过改变某些节点的颜色来恢复平衡。 2. **单旋 (Left Rotation / Right Rotation)**: 当简单变色无法解决问题时,可以通过一次旋转重新排列节点位置并配合变色完成修复。 3. **双旋 (Double Rotation)**: 对于更复杂的情形,先进行一次反方向的单旋再做正向单旋即可解决冲突。 下面是一个简单的 C 实现框架: ```c void insertFixup(RBTree T, RBTree z) { while (z->parent && z->parent->color == RED) { // 若祖父存在且父亲为红 if (z->parent == z->parent->parent->lchild) { // P 是 G 的左儿子 RBTree y = z->parent->parent->rchild; // 获取叔叔 U if (y != NULL && y->color == RED) { // Case 1: 叔叔也是红色 -> 变色 z->parent->color = BLACK; y->color = BLACK; z->parent->parent->color = RED; z = z->parent->parent; } else { // Uncle is black or null if (z == z->parent->rchild) { // Case 2: Triangular case -> Left Rotate first z = z->parent; rotateLeft(T, z); } // Case 3: Linear case -> Recolor and Right Rotate z->parent->color = BLACK; z->parent->parent->color = RED; rotateRight(T, z->parent->parent); } } else { // Mirror image of above code ... } } T->root->color = BLACK; // Ensure root remains Black after all operations. } ``` --- ### 查找操作 由于红黑树本质上仍是一棵二叉搜索树,因此查找逻辑与普通的 BST 完全一致。只需从根节点出发沿路比较目标值大小直至找到匹配项或者到达叶子为止。 ```c RBTree search(RBTree node, DataType key) { if (!node || node->data == key) return node; if (key < node->data) return search(node->lchild, key); return search(node->rchild, key); } ``` --- ### 删除操作概述 删除操作相对较为复杂,涉及多种情形判断及相应的结构调整措施。大致流程包括定位待删节点、替换其后代关系以及最后一步针对剩余不平衡现象作出修正处理等环节。 ---
评论 144
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_麦麦_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值