一、什么是红黑树
(一)红黑树的由来
由于BST树可能因大量的插入或删除操作导致树逐渐退化(最坏情况下退化成链表),从而使其相关操作的效率降低(最坏的情况下,降低到O(n))
如何能让其各个操作的效率一直维持在O(logn)呢?AVL树和红黑树便应运而生
AVL树和红黑树相较于二叉搜索树来说,在进行插入和删除操作后,会进行平衡检测,一旦发生失衡,则立即调整,使其重新变得平衡,所以AVL树一般可以称为平衡二叉搜索树(题目中若未特别说明,则默认是AVL树),红黑树也一样
(二)红黑树的基本性质
性质1:红黑树是一棵二叉搜索树(口诀:左根右)
性质2:根节点和叶子节点都必须是黑色(口诀:根叶黑)
注意:红黑树里面所提及的叶子节点不是度为 0 的节点,而是特指 NULL节点
性质3:红色节点的左右孩子都是黑色(口诀:不红红)
换句话说,红黑树中不能出现连续的两个红节点
性质4:从任意一个节点出发,到其所有可达的叶子节点的每条路径上,包含的黑色节点数量完全相同(口诀:路黑同)

(三)红黑树的核心平衡结论
任意节点到其最远叶子节点的最长路径长度,不超过最短路径长度的 2 倍;
也就是说,任意节点的左右子树高度差不超过2倍
证明:假设某条最短路径的黑色节点数为 h,根据性质4,所有路径的黑色节点数都是 h;根据性质3,红色节点不能连续出现,因此最长路径只能是黑红交替,最多包含 h 个红色节点,总路径长度为 2h

二、红黑树相关操作及代码实现
(一)红黑树的节点定义
1.有效节点
cpp
typedef int ELEMTYPE;
// 节点颜色枚举:红色、黑色
typedef enum { RED, BLACK } ColorType;
// 红黑树有效节点结构体设计
typedef struct RBNode
{
ELEMTYPE data; // 1.数据域:存储节点数值
struct RBNode* leftchild; // 2.左孩子指针域
struct RBNode* rightchild; // 3.右孩子指针域
struct RBNode* parent; // 4.双亲指针域
ColorType color; // 5.节点颜色属性
}RBNode;
2.辅助节点
cpp
//红黑树辅助节点结构体设计
typedef struct RBTree
{
RBNode* root; // 根节点指针域
}RBTree;
(二)工具函数
1.购买新节点
默认购买的新节点颜色为红色。
原因:
如果新节点为黑色:会直接改变当前路径的黑色节点总数,100%破坏性质4(路黑同),无论父节点是什么颜色,插入后都必须进行平衡修复,无任何例外情况。

如果新节点为红色:仅可能破坏性质3(不红红);如果新节点的父节点是黑色,不违反任何红黑树规则,无需进行平衡调整,直接插入成功,存在免调整场景。

但是,新节点为红色,并非一定都是免调整场景,也存在需要进行平衡调整的情况

总结:新购买的节点,一般默认颜色为红色;因为和默认黑色相比,默认红色有一定概率无需进行平衡调整,可以直接结束;而默认黑色,则一定需要进行平衡调整,因为一定会破坏性质4(路黑同)
cpp
// 购买新节点
RBNode* BuyNode(ELEMTYPE val)
{
RBNode* pnewnode = (RBNode*)malloc(sizeof(RBNode));
if (NULL == pnewnode)
exit(EXIT_FAILURE); // 内存申请失败,直接退出程序
pnewnode->data = val;
pnewnode->leftchild = NULL;
pnewnode->rightchild = NULL;
pnewnode->parent = NULL;
pnewnode->color = RED; // 新节点默认红色
return pnewnode;
}
2.左旋

红黑树相较于AVL树,它不需要修正结点的高度,但是需注意是否要修正双亲指针parent
cpp
// 左旋
RBNode* LeftRotate(RBNode* node)
{
// 左旋:让失衡节点node旋转到其右孩子的左孩子位置
assert(node != NULL);
// 申请两个指针child和grandchild分别指向对应的那个孩子和孙子
RBNode* child = node->rightchild; // 右孩子
RBNode* grandchild = child->leftchild; // 右孩子的左孩子
// 默认grandchild存在
// 如果grandchild真的存在,将它收为失衡节点node的右孩
// 如果grandchild不存在(==NULL),则将该空节点收为失衡节点node的右孩
node->rightchild = grandchild;
if (grandchild != NULL)
grandchild->parent = node;
// 此时可以让node直接旋转下来,让child接收
child->leftchild = node;
node->parent = child;
// 红黑树相较于AVL树,它不需要修正结点的高度,但是需注意是否要修正双亲指针parent
// 返回旋转后的新的根节点
return child;
}
3.右旋

红黑树相较于AVL树,它不需要修正结点的高度,但是需注意是否要修正双亲指针parent
cpp
// 右旋
RBNode* RightRotate(RBNode* node)
{
// 右旋:让失衡节点node旋转到其左孩子的右孩子位置
assert(node != NULL);
// 申请两个指针child和grandchild分别指向对应的孩子和孙子
RBNode* child = node->leftchild; // 左孩子
RBNode* grandchild = child->rightchild; // 左孩子的右孩子
// 默认grandchild存在
// 如果grandchild真的存在,将它收为失衡节点node的左孩
// 如果grandchild不存在(== NULL),则将该空节点收为失衡节点node的左孩
node->leftchild = grandchild;
if (grandchild != NULL)
grandchild->parent = node;
// 此时可以让node直接旋转下来,让child来接收
child->rightchild = node;
node->parent = child;
// 红黑树相较于AVL树,它不需要修正结点的高度,但是需注意是否要修正双亲指针parent
// 返回旋转后的新的根节点
return child;
}
4.插入操作的调整函数
(1)普通版
cpp
// 插入操作的调整函数
// 在辅助节点pTree->root指向的红黑树上,考虑node节点是否导致了红黑树失衡
void Insert_Adjust(RBTree* pTree, RBNode* node)
{
// 0.断言
assert(pTree != NULL);
// 1.获取node节点的父节点、爷爷节点
RBNode* father = node->parent;
RBNode* grandfather = father != NULL ? father->parent : NULL;
// 2.判断node节点的父节点是否存在
// 2.1 父节点不存在,则说明node节点根节点,根节点必须黑色
if (NULL == father)
{
node->color = BLACK;
return;
}
// 2.2 父节点存在,观察父节点颜色,若父节点颜色是黑色 => 无需调整,直接结束
if (father->color == BLACK)
return;
// 执行至此:说明父节点存在且为红色,间接说明grandfater一定存在,因为父节点是红色不可能作为根节点(性质2:根叶黑)
// 3.获取叔叔节点
RBNode* uncle = grandfather->leftchild == father ? grandfather->rightchild : grandfather->leftchild;
// 3.1 叔叔节点存在且为红色 => 叔父爷变色,然后将其爷爷节点看作新插入的节点,向上递归修复
if (NULL != uncle && RED == uncle->color)
{
// 父、叔叔变黑,爷爷变红
uncle->color = BLACK;
father->color = BLACK;
grandfather->color = RED;
// 以爷爷节点为新节点,向上回溯修复上层失衡
return Insert_Adjust(pTree, grandfather);
}
// 3.2 叔叔节点不存在 或者 存在但颜色是黑色 => 通过三个节点(新节点node,父节点,爷爷节点)来判断失衡类型,调用对应的旋转策略+变色操作
// 左分支失衡:父亲是爷爷的左孩子
if (grandfather->leftchild == father) // L()
{
if (father->leftchild == node) // LL
{
// 记录太爷爷,用于旋转后挂载上层节点
RBNode* great_grandfather = grandfather->parent; // 太爷爷可能存在,可能不存在
// 如果太爷爷存在,则让太爷爷来接收旋转函数的返回节点
// 如果太爷爷不存在,也就说明原本爷爷节点就是根节点,此时旋转函数的返回值让辅助节点接收即可
// 以爷爷为基准右旋
RBNode* ptr = RightRotate(grandfather);
// 处理上层挂载关系
if (NULL == great_grandfather)
{
pTree->root = ptr;
ptr->parent = NULL;
}
else
{
if (ptr->data < great_grandfather->data)
great_grandfather->leftchild = ptr;
else
great_grandfather->rightchild = ptr;
ptr->parent = great_grandfather;
}
//变色(找谁绕着谁转)
father->color = BLACK;
grandfather->color = RED;
}
else //LR
{
// 预先变色(以第二次旋转为准来找谁绕着谁转)(旋转和变色先后顺序不影响)
grandfather->color = RED;
node->color = BLACK;
RBNode* great_grandfather = grandfather->parent; //太爷爷有可能存在,有可能不存在
// 如果太爷爷存在,则让太爷爷来接收旋转函数的返回节点
// 如果太爷爷不存在,也就说明原本爷爷节点就是根节点,此时旋转函数的返回值让辅助节点接收即可
// 以爷爷节点为基准 执行先左旋再右旋
grandfather->leftchild = LeftRotate(father); // 先左旋爷爷节点的左孩子,也就是父节点
RBNode* ptr = RightRotate(grandfather); // 再右旋爷爷节点自己
// 挂载上层节点
if (great_grandfather == NULL)
{
pTree->root = ptr;
ptr->parent = NULL;
}
else
{
if (ptr->data < great_grandfather->data)
great_grandfather->leftchild = ptr;
else
great_grandfather->rightchild = ptr;
ptr->parent = great_grandfather;
}
}
}
// 右分支失衡:父亲是爷爷的右孩子
if (grandfather->rightchild == father) // ()R
{
if (father->rightchild == node) //RR
{
RBNode* great_grandfather = grandfather->parent; //太爷爷有可能存在,有可能不存在
// 如果太爷爷存在,则让太爷爷来接收旋转函数的返回节点
// 如果太爷爷不存在,也就说明原本爷爷节点就是根节点,此时旋转函数的返回值让辅助节点接收即可
// 以爷爷为基准左旋
RBNode* ptr = LeftRotate(grandfather);
// 挂载上层节点
if (great_grandfather == NULL)
{
pTree->root = ptr;
ptr->parent = NULL;
}
else
{
if (ptr->data < great_grandfather->data)
great_grandfather->leftchild = ptr;
else
great_grandfather->rightchild = ptr;
ptr->parent = great_grandfather;
}
//变色(找谁绕着谁转)
father->color = BLACK;
grandfather->color = RED;
}
else //RL
{
// 预先变色(以第二次旋转为准来找谁绕着谁转)(旋转和变色先后顺序不影响)
grandfather->color = RED;
node->color = BLACK;
RBNode* great_grandfather = grandfather->parent; //太爷爷有可能存在,有可能不存在
// 如果太爷爷存在,则让太爷爷来接收旋转函数的返回节点
// 如果太爷爷不存在,也就说明原本爷爷节点就是根节点,此时旋转函数的返回值让辅助节点接收即可
//以爷爷节点为基准 执行先右旋再左旋
grandfather->rightchild = RightRotate(father); // 先右旋爷爷节点的右孩子,也就是父节点
RBNode* ptr = LeftRotate(grandfather); //再左旋爷爷节点自己
// 挂载上层节点
if (great_grandfather == NULL)
{
pTree->root = ptr;
ptr->parent = NULL;
}
else
{
if (ptr->data < great_grandfather->data)
great_grandfather->leftchild = ptr;
else
great_grandfather->rightchild = ptr;
ptr->parent = great_grandfather;
}
}
}
return;
}
(2)优化版
普通版代码旋转变色部分存在大量重复代码,优化版将重复的节点挂载 部分作为工具函数 Return_value_receive_by,大幅精简冗余代码,使结构更清晰明了
cpp
// 让旋转函数的返回值,被正确的人来接收
void Return_value_receive_by(RBNode* ptr, RBNode* greatgrandfather, RBTree* pTree)
{
//太爷爷不存在 => 辅助节点来接收ptr
if (greatgrandfather == NULL)
{
pTree->root = ptr;
ptr->parent = NULL;
}
//太爷爷存在 => 太爷爷来接收ptr
else
{
if (ptr->data < greatgrandfather->data)
greatgrandfather->leftchild = ptr;
else
greatgrandfather->rightchild = ptr;
ptr->parent = greatgrandfather;
}
}
cpp
// 插入操作的调整函数
// 在辅助节点pTree->root指向的红黑树上,考虑node节点是否导致了红黑树失衡
void Insert_Adjust(RBTree* pTree, RBNode* node)
{
// 0.断言
assert(pTree != NULL);
// 1.获取node节点的父节点、爷爷节点
RBNode* father = node->parent;
RBNode* grandfather = father != NULL ? father->parent : NULL;
// 2.判断node节点的父节点是否存在
// 2.1 父节点不存在,则说明node节点根节点,根节点必须黑色
if (NULL == father)
{
node->color = BLACK;
return;
}
// 2.2 父节点存在,观察父节点颜色,若父节点颜色是黑色 => 无需调整,直接结束
if (father->color == BLACK)
return;
// 执行至此:说明父节点存在且为红色,间接说明grandfater一定存在,因为父节点是红色不可能作为根节点(性质2:根叶黑)
// 3.获取叔叔节点
RBNode* uncle = grandfather->leftchild == father ? grandfather->rightchild : grandfather->leftchild;
// 3.1 叔叔节点存在且为红色 => 叔父爷变色,然后将其爷爷节点看作新插入的节点,向上递归修复
if (NULL != uncle && RED == uncle->color)
{
// 父、叔叔变黑,爷爷变红
uncle->color = BLACK;
father->color = BLACK;
grandfather->color = RED;
// 以爷爷节点为新节点,向上回溯修复上层失衡
return Insert_Adjust(pTree, grandfather);
}
// 3.2 叔叔节点不存在 或者 存在但颜色是黑色 => 通过三个节点(新节点node,父节点,爷爷节点)来判断失衡类型,调用对应的旋转策略+变色操作
// 左分支失衡:父亲是爷爷的左孩子
if (grandfather->leftchild == father) //L()
{
if (father->leftchild == node) //LL
{
// 记录太爷爷,用于旋转后挂载上层节点
RBNode* great_grandfather = grandfather->parent; // 太爷爷可能存在,可能不存在
// 如果太爷爷存在,则让太爷爷来接收旋转函数的返回节点
// 如果太爷爷不存在,也就说明原本爷爷节点就是根节点,此时旋转函数的返回值让辅助节点接收即可
// 以爷爷为基准右旋
RBNode* ptr = RightRotate(grandfather);
// 调用工具函数统一处理挂载
Return_value_receive_by(ptr, great_grandfather, pTree);
//变色(找谁绕着谁转)
father->color = BLACK;
grandfather->color = RED;
}
else //LR
{
// 预先变色(以第二次旋转为准来找谁绕着谁转)(旋转和变色先后顺序不影响)
grandfather->color = RED;
node->color = BLACK;
RBNode* great_grandfather = grandfather->parent; //太爷爷有可能存在,有可能不存在
// 如果太爷爷存在,则让太爷爷来接收旋转函数的返回节点
// 如果太爷爷不存在,也就说明原本爷爷节点就是根节点,此时旋转函数的返回值让辅助节点接收即可
// 以爷爷节点为基准 执行先左旋再右旋
grandfather->leftchild = LeftRotate(father); // 先左旋爷爷节点的左孩子,也就是父节点
RBNode* ptr = RightRotate(grandfather); // 再右旋爷爷节点自己
// 调用工具函数统一处理挂载
Return_value_receive_by(ptr, great_grandfather, pTree);
}
}
// 右分支失衡:父亲是爷爷的右孩子
if (grandfather->rightchild == father) // ()R
{
if (father->rightchild == node) //RR
{
RBNode* great_grandfather = grandfather->parent; //太爷爷有可能存在,有可能不存在
// 如果太爷爷存在,则让太爷爷来接收旋转函数的返回节点
// 如果太爷爷不存在,也就说明原本爷爷节点就是根节点,此时旋转函数的返回值让辅助节点接收即可
// 以爷爷为基准左旋
RBNode* ptr = LeftRotate(grandfather);
// 调用工具函数统一处理挂载
Return_value_receive_by(ptr, great_grandfather, pTree);
//变色(找谁绕着谁转)
father->color = BLACK;
grandfather->color = RED;
}
else //RL
{
// 预先变色(以第二次旋转为准来找谁绕着谁转)(旋转和变色先后顺序不影响)
grandfather->color = RED;
node->color = BLACK;
RBNode* great_grandfather = grandfather->parent; //太爷爷有可能存在,有可能不存在
// 如果太爷爷存在,则让太爷爷来接收旋转函数的返回节点
// 如果太爷爷不存在,也就说明原本爷爷节点就是根节点,此时旋转函数的返回值让辅助节点接收即可
//以爷爷节点为基准 执行先右旋再左旋
grandfather->rightchild = RightRotate(father); // 先右旋爷爷节点的右孩子,也就是父节点
RBNode* ptr = LeftRotate(grandfather); //再左旋爷爷节点自己
// 调用工具函数统一处理挂载
Return_value_receive_by(ptr, great_grandfather, pTree);
}
}
return;
}
5.删除操作的调整函数
cpp
// 删除操作的调整函数
void Delete_Adjust(RBTree* pTree, RBNode* node) // node为0/1分支待删节点
{
assert(pTree != NULL);
// 1.申请两个指针child和father,分别指向node的孩子节点和父节点
RBNode* father = node->parent; // 可能为NULL
RBNode* child = node->leftchild != NULL ? node->leftchild : node->rightchild;
// 2.处理1分支节点
if (child != NULL) // 此时node一定是1分支结点
{
node->data = child->data;
node->leftchild = node->rightchild = NULL;
free(child);
child = NULL;
return;
}
// 3.处理0分支节点
else
{
// 3.1 0分支红色节点:直接删除,无失衡无需调整
if (node->color == RED)
{
if (node == father->leftchild) father->leftchild = NULL;
else father->rightchild = NULL;
free(node);
node = NULL;
return;
}
// 3.2 0分支黑色节点:删除会缺失黑色高度,必然失衡,需要调整
else
{
// 根节点直接删除,无需调整
if (father == NULL)
{
pTree->root = NULL;
free(node);
node = NULL;
return;
}
// 父节点存在,必有兄弟节点,根据兄弟节点颜色分类调整
else
{
// 获取当前节点的兄弟节点
RBNode* sibling = node == father->leftchild ? father->rightchild : father->leftchild;
// 情况1:兄弟节点为红色
if (sibling->color == RED)
{
//父兄变色:父变红、兄弟变黑
father->color = RED;
sibling->color = BLACK;
//让父节点向待删除节点一侧进行单旋
RBNode* grandfather = father->parent;
RBNode* ptr = father->leftchild == node ? LeftRotate(father) : RightRotate(father);
Return_value_receive_by(ptr, grandfather, pTree);
// 旋转后更新兄弟节点,此时新兄弟一定为黑色
sibling = father->leftchild == node ? father->rightchild : father->leftchild;
}
// 执行至此:兄弟节点一定是黑色(原生黑 / 红兄弟旋转后变黑)
// 查找黑兄弟是否存在红色孩子
RBNode* redchild = NULL;
if (sibling->leftchild != NULL && sibling->color == RED) redchild = sibling->leftchild;
else if (sibling->rightchild != NULL && sibling->color == RED) redchild = sibling->rightchild;
else redchild = NULL;
// 情况2:黑兄弟无红色孩子,向上传递失衡
if (redchild == NULL)
{
// 兄弟变红,抵消删除缺失的黑色,黑色缺陷向上传递给父节点
sibling->color = RED;
// 父节点是根节点,直接结束
if (father->parent == NULL)
{
if (node == father->leftchild) father->leftchild = NULL;
else father->rightchild = NULL;
free(node);
node = NULL;
return;
}
else
{
// 父节点为红色:父变黑补足黑色高度,结束调整
if (father->color == RED)
{
father->color = BLACK;
if (node == father->leftchild) father->leftchild = NULL;
else father->rightchild = NULL;
free(node);
node = NULL;
return;
}
// 父节点为黑色:缺陷继续向上递归传递
else
{
if (node == father->leftchild) father->leftchild = NULL;
else father->rightchild = NULL;
free(node);
node = NULL;
Delete_Adjust(pTree, father);
}
}
}
// 情况3:黑兄弟存在红色孩子,旋转+变色彻底修复失衡
else
{
RBNode* grandfather = father->parent;
// 待删节点是父节点左孩子:左删场景
if (node == father->leftchild)
{
// RR型:兄弟右孩子为红,单左旋修复
if (sibling->rightchild == redchild)
{
sibling->color = father->color;
father->color = BLACK;
redchild->color = BLACK;
RBNode* ptr = LeftRotate(father);
Return_value_receive_by(ptr, grandfather, pTree);
}
// RL型:兄弟左孩子为红,先右后左双旋修复
else
{
redchild->color = father->color;
father->color = BLACK;
sibling->leftchild = RightRotate(sibling);
RBNode* ptr = LeftRotate(father);
Return_value_receive_by(ptr, grandfather, pTree);
}
}
// 待删节点是父节点右孩子:右删场景
else
{
// LL型:兄弟左孩子为红,单右旋修复
if (sibling->leftchild == redchild)
{
sibling->color = father->color;
father->color = BLACK;
redchild->color = BLACK;
RBNode* ptr = RightRotate(father);
Return_value_receive_by(ptr, grandfather, pTree);
}
// LR型:兄弟右孩子为红,先左后右双旋修复
else
{
redchild->color = father->color;
father->color = BLACK;
sibling->rightchild = LeftRotate(sibling);
RBNode* ptr = RightRotate(father);
Return_value_receive_by(ptr, grandfather, pTree);
}
}
// 移除最终待删除节点
if (node == father->leftchild) father->leftchild = NULL;
else father->rightchild = NULL;
free(node);
node = NULL;
}
}
}
}
}
(三)普通函数
1.初始化
cpp
// 初始化
void Init_AVL(RBTree* pTree)
{
// 断言:树结构体指针非空
assert(pTree != NULL);
// 初始化根节点为空树
pTree->root = NULL;
}
2.插入
逻辑梳理:
1.判断是否需要插入
插入前必须判定目标值节点是否存在,若树中已存在对应值节点,无需重复插入,直接结束流程
- 从根节点开始遍历,借助指针 p 向下移动查找,指针 pp 实时记录 p 的上一级父节点,用于后续新节点挂载
- 循环终止判定:若 p 不为空且匹配到目标值,代表节点已存在,放弃插入;若 p 遍历至 NULL,代表节点不存在,允许插入新节点。
2.插入新节点
- 购买新节点(默认颜色是红色)
- 如果原本的红黑树是一个空树,则直接购买新节点并将颜色置黑,然后让辅助节点指向新节点(注意:如果原本是一颗空树,则无需提前探测val是否存在)
- 如果原本的红黑树不是一个空树,则然后新节点直接挂载到遍历留存的父节点 pp 的左/右子树,并完善双亲指针关系。
3.插入后失衡判定与修复
此时新节点pnewnode已经落位(若原树不是空树,则pnewnode此时插入在pp的下面)
- 插入后未失衡情况
新节点的父节点存在且为黑色,不违反任何红黑树性质,直接结束无需修复
- 插入后失衡情况
情况一:新节点无父节点,是整棵树的根节点,违反性质2(根叶黑)
直接将新节点从红色改为黑色即可修复
情况二:新节点的父节点存在且为红色,叔叔节点存在且为红色,违反性质3(不红红)
父、叔叔节点变黑,爷爷节点变红,将爷爷节点当作新插入节点,向上递归回溯修复上层失衡。
情况三:新节点的父节点存在且为红色,叔叔节点不存在或为黑色,违反性质3(不红红)
通过 新节点、父节点、爷爷节点 判定失衡类型(LL / RR / LR / RL),对应执行单旋/双旋操作,再配合配合变色机制修复
红黑树插入的变色机制:
如果是单旋:找旋转点和旋转中心点进行变色处理(换句话说,寻找谁绕着谁转,将这两个节点进行变色)
如果是双旋:以第二次旋转为基准,寻找谁绕着谁转,将这两个节点进行变色

cpp
// 插入
bool Insert_RB(RBTree* pTree, ELEMTYPE val)
{
// 0.断言
assert(pTree != NULL);
// 0.5 提前处理特殊情况:当前是空树,直接插入根节点并置黑
if (pTree->root == NULL)
{
pTree->root = BuyNode(val);
pTree->root->color = BLACK;
return true;
}
// 1.从根节点开始遍历,查找插入位置、查重,如果已经存在则不用插入
RBNode* p = pTree->root;
// 1.5 申请指针pp,默认指向NULL,pp用于记录p的父节点
RBNode* pp = NULL;
// 2.遍历寻找空位 / 判断节点是否重复
while (p != NULL && val != p->data)
{
pp = p; // 记录当前节点为父节点
// 小于当前节点向左走,大于则向右走
if (val < p->data)
p = p->leftchild;
else
p = p->rightchild;
}
//4.当while循环结束的时候,p如果为NULL,说明真的不存在val值节点,可以插入,并且一定是插入在指针pp的下面
// 4.1 循环结束,若p不为空说明节点已存在,无需重复插入,直接返回成功
if (p != NULL)
return true;
// 4.2 循环结束,若p为空说明节点不存在,需要插入
// 5.购买新节点,插入在pp的下面
RBNode* pnewnode = BuyNode(val);
if (val < pp->data)
pp->leftchild = pnewnode;
else
pp->rightchild = pnewnode;
pnewnode->parent = pp; // 维护双亲指针
// 6.插入完成,检测并修复红黑树失衡
// 无需判断是否失衡,函数内部自动适配所有场景
Insert_Adjust(pTree, pnewnode);
return true;
}
3.删除
逻辑梳理:
- 先按照BST树的逻辑查找待删除节点,找不到则无需删除
- 找到节点后,进一步区分 0分支 / 1分支 / 2分支 节点
- 如果是2分支结点,则通过狸猫换太子,将删除2分支节点问题转化为删除1分支/0分支节点问题
- 如果是0分支、1分支,统一交给调整函数处理,完成删除与平衡修复
删除失衡判定与修复:


cpp
// 删除
bool Delete_RB(RBTree* pTree, ELEMTYPE val)
{
assert(pTree != NULL);
// 1.寻找待删除节点,如果找不到则无需删除
RBNode* p = Search_RB(pTree->root, val);
if (p == NULL)
return true;
// 2.如果是2分支结点,通过"狸猫换太子"转化为0/1分支结点
if (p->leftchild != NULL && p->rightchild != NULL)
{
// 找后继节点(右子树最左节点,一定是0/1分支)
RBNode* cat = p->rightchild;
while (cat->leftchild != NULL)
cat = cat->leftchild;
// 后继值覆盖原节点,待删节点变为后继节点
p->data = cat->data;
p = cat;
}
// 3.统一处理0分支/1分支删除 + 红黑树失衡调整
Delete_Adjust(pTree, p);
return true;
}
4.查找
cpp
// 查找
RBNode* Search_RB(RBNode* root, ELEMTYPE val)
{
// 空树直接返回NULL
if (root == NULL)
return NULL;
RBNode* p = root;
// 当前节点不为空且数值不匹配,持续遍历
while (p != NULL && val != p->data)
{
// 目标值更小,向左子树查找
if (val < p->data)
p = p->leftchild;
// 目标值更大,向右子树查找
else
p = p->rightchild;
}
// 跳出循环:找到节点/遍历至空节点
return p;
}
5.打印(中序)
遍历规则详见:
cpp
// 打印(中序遍历-非递归实现)
void Show_InOrder(RBNode* root)
{
// 树为空直接返回
if (root == NULL)
return;
std::stack<RBNode*> st;
st.push(root);
bool tag = true;
while (!st.empty())
{
// tag为true时,持续遍历左子树入栈
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;
}
}
三、主函数测试
cpp
int main()
{
// 初始化
RBTree tree;
Init_AVL(&tree);
// 批量插入数据
int arr[] = { 10, 5, 15, 3, 7, 12, 18, 2, 4, 6 };
int n = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < n; i++)
{
Insert_RB(&tree, arr[i]);
}
printf("中序遍历结果:\n");
Show_InOrder(tree.root);
printf("\n\n");
// 查找
printf("========== 查找功能测试 ==========\n");
int findVal1 = 7;
int findVal2 = 99;
RBNode* ret1 = Search_RB(tree.root, findVal1);
RBNode* ret2 = Search_RB(tree.root, findVal2);
if (ret1 != NULL)
printf("查找节点 %d 成功,节点存在\n", findVal1);
else
printf("查找节点 %d 失败,节点不存在\n", findVal1);
if (ret2 != NULL)
printf("查找节点 %d 成功,节点存在\n", findVal2);
else
printf("查找节点 %d 失败,节点不存在\n", findVal2);
printf("\n");
// 删除
printf("========== 删除功能测试 ==========\n");
Delete_RB(&tree, 10); // 删除根节点(2分支,狸猫换太子)
Delete_RB(&tree, 5); // 删除中间分支节点
Delete_RB(&tree, 2); // 删除叶子节点
printf("中序遍历结果:\n");
Show_InOrder(tree.root);
printf("\n");
return 0;
}
