阅读说明:全文由浅入深、零基础友好,图文逻辑完整,文末附带代码实战 + 面试速记卡片 + 标准答案题库,可直接用于学习、复盘、面试突击。
一、开场:3张图带你30秒看懂红黑树
1.1 建立直观认知
⭐图1.1 核心对比:BST vs 红黑树
画面:左侧歪扭退化链表(BST插入1,2,3,4,5)、右侧矮胖平衡树形(红黑树同数据)。

核心结论:普通BST会退化,红黑树可以自动维持平衡。
⭐图1.2 工业级应用场景
画面:Java、C++、Linux内核三大技术栈标识。

核心结论:红黑树不是学术玩具,是TreeMap、std::map、内核调度、定时器的底层核心数据结构。
⭐图1.3 性能进化路线
BST(会退化O(n))→ 红黑树(自动平衡)→所有操作稳定 O(logn)。

1.2 一句话彻底定义红黑树
红黑树 = 带颜色约束的自平衡二叉搜索树,无论如何插入删除,增删查效率永远稳定在 O(logn)。
二、学前热身:为什么一定要用红黑树?
2.1 BST核心规则(基础回顾)
BST唯一核心规则:左小右大。
任意节点:左子树所有值 < 当前节点 < 右子树所有值;中序遍历严格升序。
⭐图2.1 标准BST结构演示:根10、左5、右15,遍历结果:5 10 15。

2.2 BST的致命缺陷
连续插入 1、2、3、4、5,BST 会变成单向链表,查询效率从 O(logn) 退化到 O(n),数据量越大越卡顿。
2.3 红黑树的解决方案
⭐图2.2 同数据红黑树平衡演示
同样有序数据,红黑树通过红黑颜色约束 + 旋转变色,始终保持矮胖平衡形态,彻底解决链表退化问题。

三、核心原理:5条铁律 + 3个调整工具
3.1 红黑树五条核心规则
核心逻辑 :用颜色限制树形,保证最长路径 ≤ 最短路径 × 2。
⭐ 【图3.1】基本红黑树样例,后续图片默认叶子节点(NULL)为黑,不额外画

规则对照表
| 编号 | 官方规则 | 通俗人话 |
|---|---|---|
| ① | 节点非红即黑 | 只有两种颜色 |
| ② | 红黑树(RBT)首先得是一棵二叉搜索树(BST) | 左 < 根 < 右 |
| ③ | 根节点、空叶子节点(NIL)都为黑 | 树顶、没有的节点都算黑色 |
| ④ | 红节点的孩子都是黑节点 | 禁止红红相连 |
| ⑤ | 所有路径黑色节点数量相同 | 全局黑高统一 |
口诀:左根右、根叶黑、不红红、路黑同
工具1:右旋------左儿子上位
⭐图3.2 右旋演示 :将当前节点的左孩子提拉为父节点,原父节点下沉为右孩子。

工具2:左旋------右儿子上位
⭐图3.3 左旋演示 :将当前节点的右孩子提拉为父节点,原父节点下沉为左孩子。

工具3:变色------红黑重分配
⭐图3.4 变色演示 :叔、父、爷节点变色(红-->黑,黑-->红),将爷节点当作新插入节点重新判断

四、插入操作:看叔叔颜色,3种情况全覆盖
4.1 插入铁律
新节点永远默认红色
原因:插黑色必破坏黑高,一定会修复;插红色仅可能红红冲突,修复概率更低、开销更小。
4.2 插入唯一失衡问题
⭐图4.1 红红冲突 :父红 + 子红,违反规则④,修复核心:看叔叔节点颜色。

4.3 三种插入失衡场景
情况1:叔叔红色 → 只变色、不旋转
⭐图4.2 叔、父、爷节点变色(红-->黑,黑-->红) ,冲突上移递归修复。

情况2:叔叔黑色 + 节点在外侧(LL/RR)→ 一次旋转+变色
⭐图4.3 直线形态,右旋/左旋爷节点,父爷节点换色,直接终结修复。

情况3:叔叔黑色 + 节点在内侧(LR/RL)→ 两次旋转+变色(以第二次旋转为依据)
⭐图4.4 折线形态,先旋父节点变直线,再旋爷节点,换色修复。

4.4 插入终极口诀
插红节点 → 父黑结束
→ 父红看叔叔 → 叔红,(叔父爷)变色上推
→ 叔黑,分内外,外1旋(父爷)、内2旋(子父爷)

五、删除操作:最难考点!一张流程图吃透4种情况
5.1 删除核心判断
-
删红色节点:无需修复(不影响黑高)
-
删黑色节点:必须修复 (路径丢黑,产生双黑缺失)
⭐图5.1 删红/删黑对比:删红无事发生,删黑出现黑高缺口,需要向兄弟子树借黑补全。

5.2 修复核心目标
当你删掉一个黑色节点后,这条路径上的黑色节点就少了一个(打破了"所有路径黑节点数相同"的铁律)。为了维持平衡,我们把这缺失的一层"黑"强加在接替它的节点上,这个节点就成了背负两层黑色的**"双黑节点X"。**
消除双黑标记,让整树所有路径黑高重新统一。
5.3 删除决策流程
1. 查找并分类目标节点
-
首先使用
search函数寻找待删除节点,如果未找到则无需操作。 -
若找到该节点,需要根据其子节点的数量将其分类为:零分支(0个孩子)、单分支(1个孩子)或双分支(2个孩子)。
2. 不同分支情况的删除策略
根据目标节点的分支数,红黑树采取不同的处理方案:
-
双分支节点(2个孩子):
-
核心思想是**"狸猫换太子"**。
-
通过寻找替换节点(前驱或后继),将复杂的双分支删除转化为相对简单的"零分支或单分支"删除问题。基础处理逻辑与普通二叉搜索树(BST)相同,仅需追加平衡判断。
-
-
单分支节点(1个孩子):
-
节点特征推导: 在红黑树中,单分支节点本质上就是带有一个孩子的节点。这种节点绝对不可能是红色 。如果是红色会破坏路黑同,如果它是黑色,那么它唯一的孩子必然是红色 ,并且这个红孩子绝对不会再有下一代。因此,单分支节点与其孩子必定是"一黑一红"的组合。
-
删除方案: 直接让其红色的孩子节点顶替上来,并将其颜色染黑即可。在实际编写代码时,通常的做法是将这个单孩子节点的值拷贝给待删除节点,然后转而去删除那个单独的孩子节点。
-
-
零分支节点(0个孩子/叶子节点):
- 虽然基础摘除动作和 BST 一样,但这里是红黑树删除操作中最复杂、最麻烦的情况,是整个删除算法的"难点大头"。
⭐【图5.2】删除双分支节点示例
狸猫换太子:删除双分支节点转为删除单分支/零分支节点

⭐【图5.2】删除单分支节点示例

⭐【图5.3】删除零分支节点示例
- 待删除节点有黑兄弟且黑兄弟有红孩子节点

- 待删除节点有红兄弟
⭐【示例一】

⭐【示例二】

**⭐**5.4 删除决策流程图

六、完整可运行C源码(可直接复制测试)
6.1 节点定义
cpp
typedef int ElemType;
typedef enum ColorType {
RED,
BLACK
}ColorType;
//红黑树有效节点
typedef struct RBNode {
ElemType data; //数据域
struct RBNode* leftchild; //左孩子指针域
struct RBNode* rightchild; //右孩子指针域
struct RBNode* parent; //双亲指针域
ColorType color; //节点的颜色(红/黑)
}RBNode;
//辅助节点
typedef struct RBTree {
struct RBNode* root; //根节点指针域
};
6.2 基础旋转工具
cpp
//2.左旋
RBNode* LeftRotate(RBNode* node)
{
RBNode* child = node->rightchild;
RBNode* grandchild = node->rightchild->leftchild;
node->rightchild = grandchild;
if (grandchild != NULL)
grandchild->parent = node;
child->leftchild = node;
node->parent = child;
return child;
}
//3.右旋
RBNode* RightRotate(RBNode* node)
{
RBNode* child = node->leftchild;
RBNode* grandchild = node->leftchild->rightchild;
node->leftchild = grandchild;
if (grandchild != NULL)
grandchild->parent = node;
child->rightchild = node;
node->parent = child;
return child;
}
6.3 插入 + 平衡修复
cpp
//4.插入
bool Insert_RB(RBTree* pTree, ElemType val)
{
//先把节点按照BST规则落位
//1.先申请两个指针p和pp,分别指向根节点和root
RBNode* p = pTree->root;
RBNode* pp = NULL;
//1.5 专门用来处理原本可能是一颗空树
if (pTree->root == NULL)
{
pTree->root = BuyNode(val);
pTree->root->color = BLACK;
return true;
}
//2.进入while循环,如果p指向的当前节点存在,但是值不等于val
while (p != NULL && p->data != val)
{
pp = p;
if (val < p->data)
p = p->leftchild;
else
p = p->rightchild;
}
//3.当while循环,代表着要么找到,要么没找到
//要是找到 => 不用插入了
if (p != NULL && val == p->data)
return true;
//4.此时代码指向到这一行,表示p走到了NULL,也说明val值节点里面不存在 => 往里面插入
RBNode* pnewnode = BuyNode(val);
if (pnewnode->data < pp->data)
pp->leftchild = pnewnode;
else
pp->rightchild = pnewnode;
pnewnode->parent = pp;
//插入成功了
//5.此时pnewnode节点的父节点一定存在,但是颜色有可能是红,有可能是黑
if (pp->color == RED)
{
Insert_Adjust(pTree, pnewnode);
}
return true;
}
//5.插入的平衡调整函数
void Insert_Adjust(RBTree* pTree, RBNode* node)
{
//1.如果node节点的父节点不存在(是根节点),则直接颜色变黑结束
if (node->parent == NULL)
{
node->color = BLACK;
return;
}
//如果代码执行到了这里,说明node节点的父节点存在,则进一步去看其父节点颜色
RBNode* father = node->parent;//100%存在
//2.如果该节点的父节点存在,且是黑色 => 无需调整
if (father->color == BLACK)
{
return;
}
//3.如果该节点的父节点存在,且是红色 => 违反不红红,进一步先观察其叔叔的情况
RBNode* grandfather = father->parent;//100%
RBNode* uncle = father == grandfather->leftchild ? grandfather->rightchild : grandfather->leftchild;//50%
//3.1 如果该节点的叔叔节点存在且是红色 => 叔父爷变色,然后将其爷爷节点进行相同的逻辑处理
if (uncle != NULL && uncle->color == RED)
{
uncle->color = BLACK;
father->color = BLACK;
grandfather->color = RED;
Insert_Adjust(pTree, grandfather);
return;
}
//3.2 如果该节点的叔叔节点不存在(不存在就按照空节点处理,而空节点默认黑色) 或者 该节点的叔叔节点存在且颜色是黑色
//判断LL LR RR RL
if (grandfather->leftchild == father)//L
{
if (father->leftchild == node)//LL
{
//单右旋
//太爷爷的赋值 一定要在 旋转函数的上面执行
RBNode* great_grandfather = grandfather->parent;//50% //
RBNode* tmp = RightRotate(grandfather);
Rotate_ReturnNode(pTree, great_grandfather, tmp);
//变色
father->color = BLACK;
grandfather->color = RED;
}
else//LR
{
//先左旋
grandfather->leftchild = LeftRotate(father);
//再右旋
//太爷爷的赋值 一定要在 旋转函数的上面执行
RBNode* great_grandfather = grandfather->parent;//50% //
RBNode* tmp = RightRotate(grandfather);
Rotate_ReturnNode(pTree, great_grandfather, tmp);
//变色
grandfather->color = RED;
node->color = BLACK;
}
}
if (grandfather->rightchild == father)//R
{
if (father->rightchild == node)//RR
{
//单左旋
//太爷爷的赋值 一定要在 旋转函数的上面执行
RBNode* great_grandfather = grandfather->parent;//50% //
RBNode* tmp = LeftRotate(grandfather);
Rotate_ReturnNode(pTree, great_grandfather, tmp);
//变色
father->color = BLACK;
grandfather->color = RED;
}
else//RL
{
//先右旋
grandfather->rightchild = RightRotate(father);
//再左旋
//太爷爷的赋值 一定要在 旋转函数的上面执行
RBNode* great_grandfather = grandfather->parent;//50% //
RBNode* tmp = LeftRotate(grandfather);
Rotate_ReturnNode(pTree, great_grandfather, tmp);
//变色
grandfather->color = RED;
node->color = BLACK;
}
}
return;
}
//8.旋转操作的返回节点接收处理
void Rotate_ReturnNode(RBTree* pTree, RBNode* greatgrandfather, RBNode* tmp)
{
if (greatgrandfather == NULL)
{
pTree->root = tmp;
}
else
{
if (tmp->data < greatgrandfather->data)
greatgrandfather->leftchild = tmp;
else
greatgrandfather->rightchild = tmp;
}
tmp->parent = greatgrandfather;
}
6.4 删除 + 双黑修复
cpp
//6.删除
bool Delete_RB(RBTree* pTree, ElemType val)
{
//1.先找到待删除节点
RBNode* p = Search_RB(pTree->root, val);
if (p == NULL)
return true;
//2.如果该待删除节点存在,则进一步区分其是0/1/2
//2.1 如果是2分支 => 狸猫换太子
if (p->leftchild != NULL && p->rightchild != NULL)
{
//直接后继节点来 当做 狸猫
RBNode* cat = p->rightchild;
while (cat->leftchild != NULL)
cat = cat->leftchild;
p->data = cat->data;
p = cat;
}
//2.2 如果是1分支 => 让其红色孩子顶上来,然后变黑即可
if (p->leftchild != NULL || p->rightchild != NULL)
{
RBNode* child = p->leftchild != NULL ? p->leftchild : p->rightchild;
p->data = child->data;
p->leftchild = p->rightchild = NULL;
free(child);
child = NULL;
return true;
}
//2.3 0分支删除
//1.防止该0分支节点,是根节点(如果该节点即是0分支,又是根节点,说明
// 整体树只有这一个节点) => 直接释放,然后辅助节点的root
if (p->parent == NULL)
{
free(p);
p = NULL;
pTree->root = NULL;
return true;
}
//2.如果是红色 => 直接释放,无需调整
if (p->color == RED)
{
RBNode* father = p->parent;
if (p->data < father->data)
father->leftchild = NULL;
else
father->rightchild = NULL;
free(p);
p = NULL;
return true;
}
//3.如果是黑色 => 很麻烦,不急
Delete_Adjust2(pTree, p, true);
return true;
}
//7.删除的平衡调整函数
void Delete_Adjust(RBTree* pTree, RBNode* node)
{
//1.申请两个指针 father,sibling 指向其父节点和其兄弟节点
RBNode* father = node->parent;
RBNode* sibling = node == father->leftchild ? father->rightchild : father->leftchild;
//2.兄弟节点如果是红色 => 父兄变色,然后父节点朝着待删除节点一侧进行单旋,此时待删除节点就会出现一个新的黑兄弟节点
if (sibling->color == RED)
{
father->color = RED;
sibling->color = BLACK;
RBNode* grandfather = father->parent;
RBNode* tmp = NULL;
if (node == father->leftchild)
tmp = LeftRotate(father);
else
tmp = RightRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
Delete_Adjust(pTree, node);
return;
}
else//兄弟节点如果是黑色
{
//进一步 判断其兄弟节点是否有无红孩
RBNode* redchild = NULL;
if (sibling->leftchild != NULL && sibling->leftchild->color == RED)
redchild = sibling->leftchild;
else if (sibling->rightchild != NULL && sibling->rightchild->color == RED)
redchild = sibling->rightchild;
else
redchild = NULL;
//一旦确定待删除节点是无孩黑色节点,它的兄弟也是黑色,则后续用不到待删除node节点,可以先提前释放掉
free(node);
if (father->leftchild == node)
father->leftchild = NULL;
else
father->rightchild = NULL;
if (redchild == NULL) //没有红色孩子 => 兄弟变红,然后对其父节点情况接着判断
{
sibling->color = RED;
if (father->parent = NULL)
{
return;
}
else if (father->color == RED)
{
father->color = BLACK;
return;
}
else
{
Delete_Adjust(pTree, father); //这里的father不应该被删除,只是对其兄弟做判断,然后调整
return;
}
}
else//有红色孩子 (注意:如果有两个红色孩子, 型号判定的时候 能判LL/RR就不判LR/RL)
{
//LL RR LR RL
if (father->leftchild == sibling)//L
{
if (sibling->leftchild == redchild)//LL
{
//变色: r变s s变p p变黑
redchild->color = sibling->color;
sibling->color = father->color;
father->color = BLACK;
//单右旋
RBNode* grandfather = father->parent;
RBNode* tmp = RightRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
return;
}
else//LR
{
//变色: r变p p变黑
redchild->color = father->color;
father->color = BLACK;
//先左旋
father->leftchild = LeftRotate(sibling);
//再右旋
RBNode* grandfather = father->parent;
RBNode* tmp = RightRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
return;
}
}
if (father->rightchild == sibling)//R
{
//第一个字母判断是R,则进一步要修正一下redchild的指向(有可能有两个红孩,但是redchidl默认指向左侧,也及时先判的是RL)
if (sibling->rightchild != NULL && sibling->rightchild->color == RED)
{
redchild = sibling->rightchild;
}
if (sibling->rightchild == redchild)//RR
{
//变色: r变s s变p p变黑
redchild->color = sibling->color;
sibling->color = father->color;
father->color = BLACK;
//单左旋
RBNode* grandfather = father->parent;
RBNode* tmp = LeftRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
return;
}
else//RL
{
//变色: r变p p变黑
redchild->color = father->color;
father->color = BLACK;
//先右旋
father->leftchild = RightRotate(sibling);
//再左旋
RBNode* grandfather = father->parent;
RBNode* tmp = LeftRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
return;
}
}
}
}
}
void Delete_Adjust2(RBTree* pTree, RBNode* node, bool tag)//当tag为真,node应该被删
{
//1.申请两个指针 father,sibling 指向其父节点和其兄弟节点
RBNode* father = node->parent;
RBNode* sibling = node == father->leftchild ? father->rightchild : father->leftchild;
//2.兄弟节点如果是红色 => 父兄变色,然后父节点朝着待删除节点一侧进行单旋,此时待删除节点就会出现一个新的黑兄弟节点
if (sibling->color == RED)
{
father->color = RED;
sibling->color = BLACK;
RBNode* grandfather = father->parent;
RBNode* tmp = NULL;
if (node == father->leftchild)
tmp = LeftRotate(father);
else
tmp = RightRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
Delete_Adjust2(pTree, node, true);
return;
}
else//兄弟节点如果是黑色
{
//进一步 判断其兄弟节点是否有无红孩
RBNode* redchild = NULL;
if (sibling->leftchild != NULL && sibling->leftchild->color == RED)
redchild = sibling->leftchild;
else if (sibling->rightchild != NULL && sibling->rightchild->color == RED)
redchild = sibling->rightchild;
else
redchild = NULL;
//一旦确定待删除节点是无孩黑色节点,它的兄弟也是黑色,则后续用不到待删除node节点,可以先提前释放掉
if (tag)
{
free(node);
if (father->leftchild == node)
father->leftchild = NULL;
else
father->rightchild = NULL;
}
if (redchild == NULL) //没有红色孩子 => 兄弟变红,然后对其父节点情况接着判断
{
sibling->color = RED;
if (father->parent == NULL) //情况一:father是根
{
return;
}
else if (father->color == RED)//情况二:father不是根,但是是红色
{
father->color = BLACK;
return;
}
else//情况三:father不是根,但是是黑色
{
Delete_Adjust2(pTree, father, false); //这里的father不应该被删除,只是对其兄弟做判断,然后调整
return;
}
}
else//有红色孩子 (注意:如果有两个红色孩子, 型号判定的时候 能判LL/RR就不判LR/RL)
{
//LL RR LR RL
if (father->leftchild == sibling)//L
{
if (sibling->leftchild == redchild)//LL
{
//变色: r变s s变p p变黑
redchild->color = sibling->color;
sibling->color = father->color;
father->color = BLACK;
//单右旋
RBNode* grandfather = father->parent;
RBNode* tmp = RightRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
return;
}
else//LR
{
//变色: r变p p变黑
redchild->color = father->color;
father->color = BLACK;
//先左旋
father->leftchild = LeftRotate(sibling);
//再右旋
RBNode* grandfather = father->parent;
RBNode* tmp = RightRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
return;
}
}
if (father->rightchild == sibling)//R
{
//第一个字母判断是R,则进一步要修正一下redchild的指向(有可能有两个红孩,但是redchidl默认指向左侧,也及时先判的是RL)
if (sibling->rightchild != NULL && sibling->rightchild->color == RED)
{
redchild = sibling->rightchild;
}
if (sibling->rightchild == redchild)//RR
{
//变色: r变s s变p p变黑
redchild->color = sibling->color;
sibling->color = father->color;
father->color = BLACK;
//单左旋
RBNode* grandfather = father->parent;
RBNode* tmp = LeftRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
return;
}
else//RL
{
//变色: r变p p变黑
redchild->color = father->color;
father->color = BLACK;
//先右旋
father->leftchild = RightRotate(sibling);
//再左旋
RBNode* grandfather = father->parent;
RBNode* tmp = LeftRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
return;
}
}
}
}
}
6.5 遍历验证 (中序)
cpp
//3.打印(中序非递归)
#include <stack>
void Show_InOrderRB(RBNode* root)
{
if (root == NULL)
return;
std::stack<RBNode* >st;
st.push(root);
bool tag = true;
while (!st.empty())
{
while (tag && st.top()->leftchild != NULL)
st.push(st.top()->leftchild);
RBNode* tmp = st.top();
st.pop();
printf("%d ", tmp->data);
if (tmp->rightchild != NULL)
{
st.push(tmp->rightchild);
tag = true;
}
else
{
tag = false;
}
}
}
附录A:高频面试题 + 标准答案
一、基础概念题
Q1:红黑树五大性质?
答:①节点非红即黑;②左<根<右;③根、空叶子节点为黑色;④红节点孩子必黑;⑤所有路径黑高数量一致。
Q2:新节点为什么默认红色?
答:黑色节点会破坏全局黑高,必定触发修复;红色节点仅可能红红冲突,修复开销最小。
Q3:根节点为什么必须黑色?
答:统一全局黑高基准,避免递归上浮后根节点变红,保证整树平衡规则生效。
二、原理深度题
Q4:为什么最长路径不超过最短路径2倍?
答:无连续红节点,最短路径全黑、最长路径红黑交替,同等黑高下,长度最大比例为 1:2。
Q5:插入和删除失衡的区别?
答:插入只破坏红红规则;删除破坏黑高平衡,是红黑树最复杂场景。
三、对比选型题
Q6:红黑树和AVL树区别与选型?
答:AVL严格平衡、查询快、增删开销大;红黑树弱平衡、旋转极少、读写均衡。频繁增删选红黑树,静态查询选AVL树。
Q7:红黑树和哈希表对比?
答:哈希表O(1)无序;红黑树O(logn)有序、支持区间遍历。要速度选哈希表,要有序稳定选红黑树。
四、工程应用题
Q8:TreeMap为什么用红黑树不用AVL?
答:集合类频繁增删,红黑树旋转次数更少、写性能更强,综合性能更优。
Q9:Linux内核为什么用红黑树?
答:内核资源动态增减频繁,需要稳定O(logn)增删查,红黑树开销低、稳定性高。
附录B:完整可测试代码
cpp
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <memory.h>
#include "RBTree.h"
//工具函数
//1.购买新节点
RBNode* BuyNode(ElemType val)
{
RBNode* pnewnode = (RBNode*)malloc(sizeof(RBNode));
if (NULL == pnewnode)
exit(EXIT_FAILURE);
memset(pnewnode, 0, sizeof(RBNode));
pnewnode->data = val;
return pnewnode;
}
//2.左旋
RBNode* LeftRotate(RBNode* node)
{
RBNode* child = node->rightchild;
RBNode* grandchild = node->rightchild->leftchild;
node->rightchild = grandchild;
if (grandchild != NULL)
grandchild->parent = node;
child->leftchild = node;
node->parent = child;
return child;
}
//3.右旋
RBNode* RightRotate(RBNode* node)
{
RBNode* child = node->leftchild;
RBNode* grandchild = node->leftchild->rightchild;
node->leftchild = grandchild;
if (grandchild != NULL)
grandchild->parent = node;
child->rightchild = node;
node->parent = child;
return child;
}
//普通函数
//1.初始化
void Init_RB(RBTree* pTree)
{
assert(pTree != NULL);
pTree->root = NULL;
}
//2.查找
RBNode* Search_RB(RBNode* root, ElemType val)
{
RBNode* p = root;
while (p != NULL && p->data != val)
{
if (val < p->data)
p = p->leftchild;
else
p = p->rightchild;
}
return p;
}
//3.打印(中序非递归)
#include <stack>
void Show_InOrderRB(RBNode* root)
{
if (root == NULL)
return;
std::stack<RBNode* >st;
st.push(root);
bool tag = true;
while (!st.empty())
{
while (tag && st.top()->leftchild != NULL)
st.push(st.top()->leftchild);
RBNode* tmp = st.top();
st.pop();
printf("%d ", tmp->data);
if (tmp->rightchild != NULL)
{
st.push(tmp->rightchild);
tag = true;
}
else
{
tag = false;
}
}
}
//4.插入
bool Insert_RB(RBTree* pTree, ElemType val)
{
//先把节点按照BST规则落位
//1.先申请两个指针p和pp,分别指向根节点和root
RBNode* p = pTree->root;
RBNode* pp = NULL;
//1.5 专门用来处理原本可能是一颗空树
if (pTree->root == NULL)
{
pTree->root = BuyNode(val);
pTree->root->color = BLACK;
return true;
}
//2.进入while循环,如果p指向的当前节点存在,但是值不等于val
while (p != NULL && p->data != val)
{
pp = p;
if (val < p->data)
p = p->leftchild;
else
p = p->rightchild;
}
//3.当while循环,代表着要么找到,要么没找到
//要是找到 => 不用插入了
if (p != NULL && val == p->data)
return true;
//4.此时代码指向到这一行,表示p走到了NULL,也说明val值节点里面不存在 => 往里面插入
RBNode* pnewnode = BuyNode(val);
if (pnewnode->data < pp->data)
pp->leftchild = pnewnode;
else
pp->rightchild = pnewnode;
pnewnode->parent = pp;
//插入成功了
//5.此时pnewnode节点的父节点一定存在,但是颜色有可能是红,有可能是黑
if (pp->color == RED)
{
Insert_Adjust(pTree, pnewnode);
}
return true;
}
#if 0
////5.插入的平衡调整函数
//void Insert_Adjust(RBTree* pTree, RBNode* node)
//{
// //1.如果node节点的父节点不存在(是根节点),则直接颜色变黑结束
// if (node->parent == NULL)
// {
// node->color = BLACK;
// return;
// }
//
// //如果代码执行到了这里,说明node节点的父节点存在,则进一步去看其父节点颜色
// RBNode* father = node->parent;//100%存在
//
// //2.如果该节点的父节点存在,且是黑色 => 无需调整
// if (father->color == BLACK)
// {
// return;
// }
//
// //3.如果该节点的父节点存在,且是红色 => 违反不红红,进一步先观察其叔叔的情况
// RBNode* grandfather = father->parent;//100%
// RBNode* uncle = father == grandfather->leftchild ? grandfather->rightchild : grandfather->leftchild;//50%
//
// //3.1 如果该节点的叔叔节点存在且是红色 => 叔父爷变色,然后将其爷爷节点进行相同的逻辑处理
// if (uncle != NULL && uncle->color == RED)
// {
// uncle->color = BLACK;
// father->color = BLACK;
// grandfather->color = RED;
//
// Insert_Adjust(pTree, grandfather);
// return;
// }
//
// //3.2 如果该节点的叔叔节点不存在(不存在就按照空节点处理,而空节点默认黑色) 或者 该节点的叔叔节点存在且颜色是黑色
// //判断LL LR RR RL
// if (grandfather->leftchild == father)//L
// {
// if (father->leftchild == node)//LL
// {
// //单右旋
//
// //太爷爷的赋值 一定要在 旋转函数的上面执行
// RBNode* great_grandfather = grandfather->parent;//50% //
// RBNode*tmp = RightRotate(grandfather);
//
//
// //太爷如果不在 辅助节点接收
// if (great_grandfather == NULL)
// {
// pTree->root = tmp;
// }
// //太爷如果在 太爷节点
// else
// {
// if (tmp->data < great_grandfather->data)
// great_grandfather->leftchild = tmp;
// else
// great_grandfather->rightchild = tmp;
// }
//
// tmp->parent = great_grandfather;
//
// //变色
// father->color = BLACK;
// grandfather->color = RED;
// }
// else//LR
// {
// //先左旋
// grandfather->leftchild = LeftRotate(father);
// //再右旋
// //太爷爷的赋值 一定要在 旋转函数的上面执行
// RBNode* great_grandfather = grandfather->parent;//50% //
// RBNode*tmp = RightRotate(grandfather);
// //太爷如果不在 辅助节点接收
// if (great_grandfather == NULL)
// {
// pTree->root = tmp;
// }
// //太爷如果在 太爷节点
// else
// {
// if (tmp->data < great_grandfather->data)
// great_grandfather->leftchild = tmp;
// else
// great_grandfather->rightchild = tmp;
// }
//
// tmp->parent = great_grandfather;
//
// //变色
// grandfather->color = RED;
// node->color = BLACK;
// }
// }
//
// if (grandfather->rightchild == father)//R
// {
// if (father->rightchild == node)//RR
// {
// //单左旋
//
// //太爷爷的赋值 一定要在 旋转函数的上面执行
// RBNode* great_grandfather = grandfather->parent;//50% //
// RBNode* tmp = LeftRotate(grandfather);
//
//
// //太爷如果不在 辅助节点接收
// if (great_grandfather == NULL)
// {
// pTree->root = tmp;
// }
// //太爷如果在 太爷节点
// else
// {
// if (tmp->data < great_grandfather->data)
// great_grandfather->leftchild = tmp;
// else
// great_grandfather->rightchild = tmp;
// }
//
// tmp->parent = great_grandfather;
//
// //变色
// father->color = BLACK;
// grandfather->color = RED;
//
// }
// else//RL
// {
// //先右旋
// grandfather->rightchild = RightRotate(father);
// //再左旋
// //太爷爷的赋值 一定要在 旋转函数的上面执行
// RBNode* great_grandfather = grandfather->parent;//50% //
// RBNode* tmp = LeftRotate(grandfather);
// //太爷如果不在 辅助节点接收
// if (great_grandfather == NULL)
// {
// pTree->root = tmp;
// }
// //太爷如果在 太爷节点
// else
// {
// if (tmp->data < great_grandfather->data)
// great_grandfather->leftchild = tmp;
// else
// great_grandfather->rightchild = tmp;
// }
//
// tmp->parent = great_grandfather;
//
// //变色
// grandfather->color = RED;
// node->color = BLACK;
// }
//
// }
//
// return;
//}
#endif
//5.插入的平衡调整函数
void Insert_Adjust(RBTree* pTree, RBNode* node)
{
//1.如果node节点的父节点不存在(是根节点),则直接颜色变黑结束
if (node->parent == NULL)
{
node->color = BLACK;
return;
}
//如果代码执行到了这里,说明node节点的父节点存在,则进一步去看其父节点颜色
RBNode* father = node->parent;//100%存在
//2.如果该节点的父节点存在,且是黑色 => 无需调整
if (father->color == BLACK)
{
return;
}
//3.如果该节点的父节点存在,且是红色 => 违反不红红,进一步先观察其叔叔的情况
RBNode* grandfather = father->parent;//100%
RBNode* uncle = father == grandfather->leftchild ? grandfather->rightchild : grandfather->leftchild;//50%
//3.1 如果该节点的叔叔节点存在且是红色 => 叔父爷变色,然后将其爷爷节点进行相同的逻辑处理
if (uncle != NULL && uncle->color == RED)
{
uncle->color = BLACK;
father->color = BLACK;
grandfather->color = RED;
Insert_Adjust(pTree, grandfather);
return;
}
//3.2 如果该节点的叔叔节点不存在(不存在就按照空节点处理,而空节点默认黑色) 或者 该节点的叔叔节点存在且颜色是黑色
//判断LL LR RR RL
if (grandfather->leftchild == father)//L
{
if (father->leftchild == node)//LL
{
//单右旋
//太爷爷的赋值 一定要在 旋转函数的上面执行
RBNode* great_grandfather = grandfather->parent;//50% //
RBNode* tmp = RightRotate(grandfather);
Rotate_ReturnNode(pTree, great_grandfather, tmp);
//变色
father->color = BLACK;
grandfather->color = RED;
}
else//LR
{
//先左旋
grandfather->leftchild = LeftRotate(father);
//再右旋
//太爷爷的赋值 一定要在 旋转函数的上面执行
RBNode* great_grandfather = grandfather->parent;//50% //
RBNode* tmp = RightRotate(grandfather);
Rotate_ReturnNode(pTree, great_grandfather, tmp);
//变色
grandfather->color = RED;
node->color = BLACK;
}
}
if (grandfather->rightchild == father)//R
{
if (father->rightchild == node)//RR
{
//单左旋
//太爷爷的赋值 一定要在 旋转函数的上面执行
RBNode* great_grandfather = grandfather->parent;//50% //
RBNode* tmp = LeftRotate(grandfather);
Rotate_ReturnNode(pTree, great_grandfather, tmp);
//变色
father->color = BLACK;
grandfather->color = RED;
}
else//RL
{
//先右旋
grandfather->rightchild = RightRotate(father);
//再左旋
//太爷爷的赋值 一定要在 旋转函数的上面执行
RBNode* great_grandfather = grandfather->parent;//50% //
RBNode* tmp = LeftRotate(grandfather);
Rotate_ReturnNode(pTree, great_grandfather, tmp);
//变色
grandfather->color = RED;
node->color = BLACK;
}
}
return;
}
//8.旋转操作的返回节点接收处理
void Rotate_ReturnNode(RBTree* pTree, RBNode* greatgrandfather, RBNode* tmp)
{
if (greatgrandfather == NULL)
{
pTree->root = tmp;
}
else
{
if (tmp->data < greatgrandfather->data)
greatgrandfather->leftchild = tmp;
else
greatgrandfather->rightchild = tmp;
}
tmp->parent = greatgrandfather;
}
//6.删除
bool Delete_RB(RBTree* pTree, ElemType val)
{
//1.先找到待删除节点
RBNode* p = Search_RB(pTree->root, val);
if (p == NULL)
return true;
//2.如果该待删除节点存在,则进一步区分其是0/1/2
//2.1 如果是2分支 => 狸猫换太子
if (p->leftchild != NULL && p->rightchild != NULL)
{
//直接后继节点来 当做 狸猫
RBNode* cat = p->rightchild;
while (cat->leftchild != NULL)
cat = cat->leftchild;
p->data = cat->data;
p = cat;
}
//2.2 如果是1分支 => 让其红色孩子顶上来,然后变黑即可
if (p->leftchild != NULL || p->rightchild != NULL)
{
RBNode* child = p->leftchild != NULL ? p->leftchild : p->rightchild;
p->data = child->data;
p->leftchild = p->rightchild = NULL;
free(child);
child = NULL;
return true;
}
//2.3 0分支删除
//1.防止该0分支节点,是根节点(如果该节点即是0分支,又是根节点,说明
// 整体树只有这一个节点) => 直接释放,然后辅助节点的root
if (p->parent == NULL)
{
free(p);
p = NULL;
pTree->root = NULL;
return true;
}
//2.如果是红色 => 直接释放,无需调整
if (p->color == RED)
{
RBNode* father = p->parent;
if (p->data < father->data)
father->leftchild = NULL;
else
father->rightchild = NULL;
free(p);
p = NULL;
return true;
}
//3.如果是黑色 => 很麻烦,不急
Delete_Adjust2(pTree, p, true);
return true;
}
//7.删除的平衡调整函数
void Delete_Adjust(RBTree* pTree, RBNode* node)
{
//1.申请两个指针 father,sibling 指向其父节点和其兄弟节点
RBNode* father = node->parent;
RBNode* sibling = node == father->leftchild ? father->rightchild : father->leftchild;
//2.兄弟节点如果是红色 => 父兄变色,然后父节点朝着待删除节点一侧进行单旋,此时待删除节点就会出现一个新的黑兄弟节点
if (sibling->color == RED)
{
father->color = RED;
sibling->color = BLACK;
RBNode* grandfather = father->parent;
RBNode* tmp = NULL;
if (node == father->leftchild)
tmp = LeftRotate(father);
else
tmp = RightRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
Delete_Adjust(pTree, node);
return;
}
else//兄弟节点如果是黑色
{
//进一步 判断其兄弟节点是否有无红孩
RBNode* redchild = NULL;
if (sibling->leftchild != NULL && sibling->leftchild->color == RED)
redchild = sibling->leftchild;
else if (sibling->rightchild != NULL && sibling->rightchild->color == RED)
redchild = sibling->rightchild;
else
redchild = NULL;
//一旦确定待删除节点是无孩黑色节点,它的兄弟也是黑色,则后续用不到待删除node节点,可以先提前释放掉
free(node);
if (father->leftchild == node)
father->leftchild = NULL;
else
father->rightchild = NULL;
if (redchild == NULL) //没有红色孩子 => 兄弟变红,然后对其父节点情况接着判断
{
sibling->color = RED;
if (father->parent = NULL)
{
return;
}
else if (father->color == RED)
{
father->color = BLACK;
return;
}
else
{
Delete_Adjust(pTree, father); //这里的father不应该被删除,只是对其兄弟做判断,然后调整
return;
}
}
else//有红色孩子 (注意:如果有两个红色孩子, 型号判定的时候 能判LL/RR就不判LR/RL)
{
//LL RR LR RL
if (father->leftchild == sibling)//L
{
if (sibling->leftchild == redchild)//LL
{
//变色: r变s s变p p变黑
redchild->color = sibling->color;
sibling->color = father->color;
father->color = BLACK;
//单右旋
RBNode* grandfather = father->parent;
RBNode* tmp = RightRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
return;
}
else//LR
{
//变色: r变p p变黑
redchild->color = father->color;
father->color = BLACK;
//先左旋
father->leftchild = LeftRotate(sibling);
//再右旋
RBNode* grandfather = father->parent;
RBNode* tmp = RightRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
return;
}
}
if (father->rightchild == sibling)//R
{
//第一个字母判断是R,则进一步要修正一下redchild的指向(有可能有两个红孩,但是redchidl默认指向左侧,也及时先判的是RL)
if (sibling->rightchild != NULL && sibling->rightchild->color == RED)
{
redchild = sibling->rightchild;
}
if (sibling->rightchild == redchild)//RR
{
//变色: r变s s变p p变黑
redchild->color = sibling->color;
sibling->color = father->color;
father->color = BLACK;
//单左旋
RBNode* grandfather = father->parent;
RBNode* tmp = LeftRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
return;
}
else//RL
{
//变色: r变p p变黑
redchild->color = father->color;
father->color = BLACK;
//先右旋
father->leftchild = RightRotate(sibling);
//再左旋
RBNode* grandfather = father->parent;
RBNode* tmp = LeftRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
return;
}
}
}
}
}
void Delete_Adjust2(RBTree* pTree, RBNode* node, bool tag)//当tag为真,node应该被删
{
//1.申请两个指针 father,sibling 指向其父节点和其兄弟节点
RBNode* father = node->parent;
RBNode* sibling = node == father->leftchild ? father->rightchild : father->leftchild;
//2.兄弟节点如果是红色 => 父兄变色,然后父节点朝着待删除节点一侧进行单旋,此时待删除节点就会出现一个新的黑兄弟节点
if (sibling->color == RED)
{
father->color = RED;
sibling->color = BLACK;
RBNode* grandfather = father->parent;
RBNode* tmp = NULL;
if (node == father->leftchild)
tmp = LeftRotate(father);
else
tmp = RightRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
Delete_Adjust2(pTree, node, true);
return;
}
else//兄弟节点如果是黑色
{
//进一步 判断其兄弟节点是否有无红孩
RBNode* redchild = NULL;
if (sibling->leftchild != NULL && sibling->leftchild->color == RED)
redchild = sibling->leftchild;
else if (sibling->rightchild != NULL && sibling->rightchild->color == RED)
redchild = sibling->rightchild;
else
redchild = NULL;
//一旦确定待删除节点是无孩黑色节点,它的兄弟也是黑色,则后续用不到待删除node节点,可以先提前释放掉
if (tag)
{
free(node);
if (father->leftchild == node)
father->leftchild = NULL;
else
father->rightchild = NULL;
}
if (redchild == NULL) //没有红色孩子 => 兄弟变红,然后对其父节点情况接着判断
{
sibling->color = RED;
if (father->parent == NULL) //情况一:father是根
{
return;
}
else if (father->color == RED)//情况二:father不是根,但是是红色
{
father->color = BLACK;
return;
}
else//情况三:father不是根,但是是黑色
{
Delete_Adjust2(pTree, father, false); //这里的father不应该被删除,只是对其兄弟做判断,然后调整
return;
}
}
else//有红色孩子 (注意:如果有两个红色孩子, 型号判定的时候 能判LL/RR就不判LR/RL)
{
//LL RR LR RL
if (father->leftchild == sibling)//L
{
if (sibling->leftchild == redchild)//LL
{
//变色: r变s s变p p变黑
redchild->color = sibling->color;
sibling->color = father->color;
father->color = BLACK;
//单右旋
RBNode* grandfather = father->parent;
RBNode* tmp = RightRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
return;
}
else//LR
{
//变色: r变p p变黑
redchild->color = father->color;
father->color = BLACK;
//先左旋
father->leftchild = LeftRotate(sibling);
//再右旋
RBNode* grandfather = father->parent;
RBNode* tmp = RightRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
return;
}
}
if (father->rightchild == sibling)//R
{
//第一个字母判断是R,则进一步要修正一下redchild的指向(有可能有两个红孩,但是redchidl默认指向左侧,也及时先判的是RL)
if (sibling->rightchild != NULL && sibling->rightchild->color == RED)
{
redchild = sibling->rightchild;
}
if (sibling->rightchild == redchild)//RR
{
//变色: r变s s变p p变黑
redchild->color = sibling->color;
sibling->color = father->color;
father->color = BLACK;
//单左旋
RBNode* grandfather = father->parent;
RBNode* tmp = LeftRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
return;
}
else//RL
{
//变色: r变p p变黑
redchild->color = father->color;
father->color = BLACK;
//先右旋
father->leftchild = RightRotate(sibling);
//再左旋
RBNode* grandfather = father->parent;
RBNode* tmp = LeftRotate(father);
Rotate_ReturnNode(pTree, grandfather, tmp);
return;
}
}
}
}
}
#if 0
int main()
{
RBTree head;
Init_RB(&head);
Insert_RB(&head, 17);
Insert_RB(&head, 18);
Insert_RB(&head, 23);
Insert_RB(&head, 34);
Insert_RB(&head, 27);
Insert_RB(&head, 15);
Insert_RB(&head, 9);
Insert_RB(&head, 6);
Insert_RB(&head, 8);
Insert_RB(&head, 5);
Insert_RB(&head, 25);
Show_InOrderRB(head.root);
printf("\n");
//Delete_RB(&head, 25);//无孩 红色节点
//Delete_RB(&head, 6);//单分支
//Delete_RB(&head, 18);//双分支 -> 转化为单分支删除
//Delete_RB(&head, 15);//双分支 -> 转化为0分支黑色节点,且兄弟是红色的删除
//Delete_RB(&head, 9);//0分支黑色节点,且兄弟是黑色且有红孩的删除 (LL型)
//Delete_RB(&head, 34);//0分支黑色节点,且兄弟是黑色且有红孩的删除 (LR型)
Delete_RB(&head, 5);
Delete_RB(&head, 6);//0分支黑色节点,且兄弟是黑色且有没有一个红孩的删除
Show_InOrderRB(head.root);
printf("\n");
return 0;
}
#endif
全文总结
-
本质:红黑树通过颜色约束实现弱平衡,用极小查询损耗换取极低的修改开销。
-
学习核心:插入修红红冲突,删除修黑高缺失,所有场景均可推导,无需死记。
-
工程价值:内存级有序数据首选结构,是Java集合、C++STL、操作系统内核的底层基石。