红黑树(RBTree):原理 + 5 大性质 + 旋转 + 插入 + 删除 + 完整工程级代码逐行解析

前言

在二叉搜索树(BST)和 AVL 树之后,红黑树 终于登场!它是计算机科学中最经典、应用最广的数据结构,没有之一。

  • C++ STL: set / map / unordered_map 底层都是红黑树
  • Java: TreeMap / TreeSet 底层都是红黑树
  • Linux 内核: 进程调度、内存管理大量使用红黑树

为什么要用红黑树?

  • AVL 树太严格:高度差必须≤1,插入删除要大量旋转,效率低
  • 红黑树:弱平衡、旋转少(最多 3 次)、性能极高

一、什么是红黑树?(一句话定义)

红黑树 = 自带颜色规则、弱平衡、自平衡二叉搜索树

通过节点颜色(红 / 黑) + 旋转 + 变色 ,保证从根到叶子的最长路径不超过最短路径的 2 倍 ,实现高效稳定的 O(logn) 操作。


二、红黑树 5 大核心性质(必须背!)

这是红黑树的灵魂,所有操作都围绕这 5 条规则:

  1. 每个节点只能是 红色 或 黑色
  2. 根节点永远是黑色
  3. 所有叶子节点(空节点)都是黑色
  4. 红色节点的两个孩子一定是黑色(不能出现连续红红)
  5. 从任意节点到其所有叶子路径上,黑色节点数量相同

核心口诀:根黑、叶黑、不红红、黑同长。


三、红黑树结构体设计

cpp 复制代码
typedef int ELEMTYPE;

// 颜色枚举
typedef enum { RED, BLACK } TYPE_COLOR;

// 红黑树节点
typedef struct RBNode
{
    ELEMTYPE val;                // 值
    struct RBNode* leftchild;    // 左孩子
    struct RBNode* parent;       // 父节点(核心!)
    struct RBNode* rightchild;   // 右孩子
    TYPE_COLOR color;            // 颜色
}RBNode;

// 红黑树管理结构
typedef struct
{
    RBNode* root;  // 树根
}RBTree;

新节点默认红色(插入红色破坏性质少,只需要处理 "红红冲突")

头文件

cpp 复制代码
#pragma once

typedef int ELEMTYPE;

typedef enum { RED, BLACK }TYPE_COLOR;

//红黑树的有效节点定义
typedef struct RBNode
{
	ELEMTYPE val;
	struct RBNode* leftchild;
	struct RBNode* parent;
	struct RBNode* rightchild;
	//bool color;//颜色//默认 true=红  black=黑 
	TYPE_COLOR color;
}RBNode;

//红黑树的辅助节点定义
typedef struct
{
	RBNode* root;
}RBTree;



//工具函数
//1.购买新节点
RBNode* BuyNode();

//2.单左旋  返回值:旋转调整之后的新的根节点  Node:插入的节点
//注意:AVL树单旋,是直接让失衡节点旋转,也就是传进的Node自身旋转
//     但是红黑树单选,是直接让Node的爷爷自身旋转,
RBNode* Left_Rotate(RBNode* Node);

//3.单右旋  返回值:旋转调整之后的新的根节点  Node:插入的节点
//注意:AVL树单旋,是直接让失衡节点旋转,也就是传进的Node自身旋转
//     但是红黑树单选,是直接让Node的爷爷自身旋转,
RBNode* Right_Rotate(RBNode* Node);


//4.插入函数的通用平衡旋转函数  //难  Node:当前插入的节点,要进行平衡判断
void Insert_Adjust_Rotate(RBTree* pTree, RBNode* Node);

//5.删除函数的通用平衡旋转函数  //难难难
//void Delete_Adjust_Rotate(RBTree* pTree, RBNode* Node);//初版 //缺陷:1.双黑上移,遇到黑色节点,此时需要告诉这个函数,第二个参数Node是否需要被删除
															  //缺陷:2.双亲指针parent的修正

void Delete_Adjust_Rotate(RBTree* pTree, RBNode* Node, bool tag);


//普通函数
//1.初始化
void Init_RBTree(RBTree* pTree);

//2.插入
bool Insert_RBTree(RBTree* pTree, ELEMTYPE val);

//3.删除
bool Delete_RBTree(RBTree* pTree, ELEMTYPE val);


//4.查询
RBNode* Search_RBTree(RBTree* pTree, ELEMTYPE val);

//5.打印(用中序遍历打印)
void Show_inOrder(RBNode* root);//递归
void Show_inOrder2(RBTree* pTree);//非递归

四、红黑树核心工具函数(基石)

1. 购买新节点

cpp 复制代码
//1.购买新节点
RBNode* BuyNode()
{
    RBNode* pnewnode = (RBNode*)malloc(sizeof(RBNode));
    if (pnewnode == NULL)
        exit(EXIT_FAILURE);
    //memset(pnewnode, 0, sizeof(RBNode));  string.h  memory.h
    pnewnode->leftchild = pnewnode->parent = pnewnode->rightchild = NULL;
    pnewnode->color = RED;//默认是红色
 
    return pnewnode;
}

2. 左旋(RR 型)

注意:红黑树旋转是旋转祖父节点!

cpp 复制代码
//2.单左旋  返回值:旋转调整之后的新的根节点  Node:插入的节点
//注意:AVL树单旋,是直接让失衡节点旋转,也就是传进的Node自身旋转
//     但是红黑树单选,是直接让Node的爷爷自身旋转,
RBNode* Left_Rotate(RBNode* Node)
{
    //1.申请两个指针用来指向它爸和它爷
    RBNode* father = Node->parent;
    RBNode* grandfather = Node->parent->parent;

    //2.让它爷左旋到它爸的左边,这里提前先把冲突节点给处理掉(有可能冲突的节点:它爸的右孩子)
    //口诀:冲突的左孩变右孩
    grandfather->rightchild = father->leftchild;
    if (grandfather->rightchild != NULL)
    {
        grandfather->rightchild->parent = grandfather;
    }

    //3.此时,它爷左旋到它爸的左边没有障碍物,则直接挪动指针即可
    father->leftchild = grandfather;
    grandfather->parent = father;

    //4.返回新的根节点(它爸)
    return father;
}

3. 右旋(LL 型)

cpp 复制代码
//3.单右旋  返回值:旋转调整之后的新的根节点  Node:插入的节点
//注意:AVL树单旋,是直接让失衡节点旋转,也就是传进的Node自身旋转
//     但是红黑树单选,是直接让Node的爷爷自身旋转,
RBNode* Right_Rotate(RBNode* Node)
{
    //1.申请两个指针用来指向它爸和它爷
    RBNode* father = Node->parent;
    RBNode* grandfather = Node->parent->parent;

    //2.让它爷右旋到它爸的右边,这里提前先把冲突节点给处理掉(有可能冲突的节点:它爸的右孩子)
    //口诀:冲突的右孩变左孩
    grandfather->leftchild = father->rightchild;
    if (grandfather->leftchild != NULL)
    {
        grandfather->leftchild->parent = grandfather;
    }

    //3.此时,它爷右旋到它爸的右边没有障碍物,则直接挪动指针即可
    father->rightchild = grandfather;
    grandfather->parent = father;

    //4.返回新的根节点(它爸)
    return father;
}

五、红黑树插入(最难!)

插入规则:

  1. 按 BST 插入,新节点红色
  2. 若父节点是黑色 → 直接结束
  3. 若父节点是红色 → 违反性质 4(红红)→ 看叔叔脸色调整

调整分 2 大场景:

场景 1:叔叔是红色

变色即可,无需旋转

  • 父、叔变黑
  • 祖父变红
  • 递归向上调整祖父

场景 2:叔叔是黑色

旋转 + 变色 分 4 种形态:LL、RR、LR、RL

cpp 复制代码
//4.插入函数的通用平衡旋转函数  //难  Node:当前插入的节点,要进行平衡判断
//终版(parent指针要管了)
void Insert_Adjust_Rotate(RBTree* pTree, RBNode* Node)
{
    //1.若判读的插入节点Node是根节点 --> 直接变黑结束
    if (Node->parent == NULL)
    {
        Node->color = BLACK;
        Node->parent = NULL;
        return;
    }

    //2.若判读的插入节点Node没有失衡(它爸是黑色) --> 无需调整,直接结束
    if (Node->parent->color == BLACK)
    {
        return;
    }

    //3.执行到这里,代表着Node不是根节点,且Node节点违反了"不红红"性质,需要看其叔叔脸色行事
    //此时,Node它自己和Node它爸以及Node它爷三个节点肯定都存在

    //4.申领三个指针,用来它爸,它爷,它叔
    RBNode* father = Node->parent;
    RBNode* grandfather = Node->parent->parent;
    RBNode* uncle = grandfather->leftchild == father ? grandfather->rightchild : grandfather->leftchild;//uncle有可能赋值为NULL

    //5.若它叔是红色
    if (uncle != NULL && uncle->color == RED)
    {
        //叔父爷变色
        uncle->color = BLACK;
        father->color = BLACK;
        grandfather->color = RED;

        //把它爷当成新的插入节点,重新进行逻辑判断
        Insert_Adjust_Rotate(pTree, grandfather);
        return;
    }
    else//它叔是黑色  --> 旋转+变色
    {
        //6.判断Node自己和它爸以及它爷,这三个节点符合哪一个型号
        if (grandfather->leftchild == father)//L
        {
            if (father->leftchild == Node)//LL
            {
                //先变色 
                father->color = BLACK;
                grandfather->color = RED;

                //再旋转
                RBNode* great_grandfather = grandfather->parent;//太爷节点 有可能存在,有可能不存在
                RBNode* ptr = Right_Rotate(Node);

                if (great_grandfather == NULL)
                {
                    pTree->root = ptr;
                    ptr->parent = NULL;
                }
                else//太爷存在 
                {
                    if (ptr->val < great_grandfather->val)
                        great_grandfather->leftchild = ptr;
                    else
                        great_grandfather->rightchild = ptr;

                    ptr->parent = great_grandfather;
                }

                return;
            }
            else//LR
            {
                //先左旋
                father->rightchild = Node->leftchild;
                if (father->rightchild != NULL)
                {
                    father->rightchild->parent = father;
                }

                Node->leftchild = father;
                father->parent = Node;
                grandfather->leftchild = Node;
                Node->parent = grandfather;

                //变色+再右旋
                grandfather->color = RED;
                Node->color = BLACK;

                RBNode* great_grandfather = grandfather->parent;
                RBNode* ptr = Right_Rotate(father);
                if (great_grandfather == NULL)
                {
                    pTree->root = ptr;
                    ptr->parent = NULL;
                }
                else
                {
                    if (ptr->val < great_grandfather->val)
                        great_grandfather->leftchild = ptr;
                    else
                        great_grandfather->rightchild = ptr;

                    ptr->parent = great_grandfather;

                }

                return;
            }
        }
        if (grandfather->rightchild == father)//R
        {
            if (father->rightchild == Node)//RR
            {
                //先变色
                father->color = BLACK;
                grandfather->color = RED;

                RBNode* great_grandfather = grandfather->parent;
                RBNode* ptr = Left_Rotate(Node);

                if (great_grandfather == NULL)
                {
                    pTree->root = ptr;
                    ptr->parent = NULL;
                }
                else//太爷存在 
                {
                    if (ptr->val < great_grandfather->val)
                        great_grandfather->leftchild = ptr;
                    else
                        great_grandfather->rightchild = ptr;


                    ptr->parent = great_grandfather;

                }


                //再旋转
                return;
            }
            else//RL
            {
                //先右旋
                father->leftchild = Node->rightchild;
                if (father->leftchild != NULL)
                {
                    father->leftchild->parent = father;
                }


                Node->rightchild = father;
                father->parent = Node;
                grandfather->rightchild = Node;
                Node->parent = grandfather;

                //变色+再左旋

                grandfather->color = RED;
                Node->color = BLACK;

                RBNode* great_grandfather = grandfather->parent;
                RBNode* ptr = Left_Rotate(father);

                if (great_grandfather == NULL)
                {
                    pTree->root = ptr;
                    ptr->parent = NULL;
                }
                else//太爷存在 
                {
                    if (ptr->val < great_grandfather->val)
                        great_grandfather->leftchild = ptr;
                    else
                        great_grandfather->rightchild = ptr;

                    ptr->parent = great_grandfather;
                }
                return;
            }
        }
    }
}
  • 完美处理所有 4 种失衡
  • 严格维护 parent 指针
  • 自动更新根节点

六、红黑树删除(数据结构天花板)

红黑树删除 = BST 删除 + 双黑调整

删除流程:

  1. BST 删除(叶子 / 单分支 / 双分支→后继替换)
  2. 若删除红色节点 → 直接结束
  3. 若删除黑色节点 → 产生 "双黑" → 触发调整

调整分 4 种情况:

  1. 兄弟红色 → 变色 + 旋转,新兄弟必黑
  2. 兄弟黑色 + 兄弟孩子全黑 → 兄弟变红,双黑上移
  3. 兄弟黑色 + 远侄子红 → 旋转 + 变色
  4. 兄弟黑色 + 近侄子红 → 旋转 + 变色
cpp 复制代码
//tag如果为真,则代表第二个参数Node是需要准备被删除的
//tag如果为假,则代表第二个参数Node是不需要被删除的,只是作为判断的媒介
void Delete_Adjust_Rotate(RBTree* pTree, RBNode* Node, bool tag)//终版 //没有缺陷
{
    //0.assert 安全性处理
    assert(pTree != NULL);
    assert(Node != NULL);

    //1.申请三个指针,分别指向待删除节点的父,兄,子
    RBNode* father = Node->parent;
    RBNode* sibling = NULL;//sibling的赋值 依赖于父节点的存在,而此时father有可能==NULL
    RBNode* child = Node->leftchild != NULL ? Node->leftchild : Node->rightchild;

    //2.此时,我们知道Node要么是没有孩子,要么有一个孩子
    //3.如果只有一个孩子(只有左孩子/右孩子)情况:--> 孩子顶替上来后变黑
    if (tag && child != NULL)//--------------
    {
        //判断此时Node是不是根节点,如果是的话,一会要修改辅助节点里的root
        if (Node->parent == NULL)
            pTree->root = child;
        else
            if (child->val < father->val)
                father->leftchild = child;
            else
                father->rightchild = child;

        child->parent = father;
        free(Node);
        child->color = BLACK;

        return;
    }
    //4.如果零个孩子 --> 要分情况处理
    else
    {
        //4.1 如果此时Node指向的节点是根节点 -->直接删除
        if (tag && Node->parent == NULL)//------------
        {
            pTree->root = NULL;
            free(Node);
            return;
        }
        else
        {
            //4.2 如果此时Node指向的节点非根节点,且颜色是红色 -->直接删除
            if (tag && Node->color == RED)//------------
            {
                //因为释放Node,要把其father指向Node的指针断开(手放开)
                if (Node == father->leftchild)//左手抓Node
                {
                    father->leftchild = NULL;
                }
                else//右手抓Node
                {
                    father->rightchild = NULL;
                }
                free(Node);
                return;
            }
            //4.3 如果此时Node指向的节点非根节点,且颜色是黑色,此时立马去判断兄弟的颜色
            else
            {
                sibling = Node == father->leftchild ? father->rightchild : father->leftchild;
                RBNode* grandfather = father->parent;//爷爷指针提前定义好,下边只要旋转都会用到

                // 如果是红色 --> 父兄变色,然后父节点朝着待删除节点一侧进行单旋,
                // 此时待删除节点就会出现一个新的兄弟节点
                if (sibling->color == RED)
                {
                    //父兄变色
                    father->color = RED;
                    sibling->color = BLACK;

                    //father节点要朝着Node节点一侧进行单旋
                    RBNode* ptr = Node == father->leftchild ? Left_Rotate(sibling->rightchild) : Right_Rotate(sibling->leftchild);
                    //ptr用来接收旋转之后的返回节点

                    //判断爷爷节点是否存在,如果不存在,则用辅助节点来接收旋转之后的返回节点
                    if (grandfather == NULL)//旋转点就是根节点,此时旋转完成之后,由pTree->root来抓住旋转后返回的新根节点
                        pTree->root = ptr;
                    //判断爷爷节点是否存在,如果存在,则用爷爷来接收旋转之后的返回节点
                    else
                        if (ptr->val < grandfather->val)
                            grandfather->leftchild = ptr;
                        else
                            grandfather->rightchild = ptr;
                    ptr->parent = grandfather;
                    Delete_Adjust_Rotate(pTree, Node, true);//这里的第二个参数Node是必须删除的,因为待删除的Node还没有被处理的
                    return;
                }
                //兄弟是黑色
                else
                {
                    //能走到这里,则代表可以先把Node删除释放掉,因为后续不需要Node了
                    if (tag)
                    {
                        if (father->leftchild == Node)
                            father->leftchild = NULL;
                        else
                            father->rightchild = NULL;
                        free(Node);
                        Node = NULL;
                    }



                    //在此时申请一个指针redchild指向兄弟的红孩(如果有两个红孩的话,默认让其指向同侧)
                    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;

                    //此时判断兄弟有没有红孩
                    //4.4 如果此时Node指向的节点非根节点,且颜色是黑色,且兄弟是黑色,且兄弟至少有一个红孩 --> 变色+旋转(注意:旋转要根据兄弟及其父子的情况去判断型号)
                    if (redchild != NULL)//至少有一个红孩-> 变色+旋转
                    {
                        //此时根据 兄弟,及其兄弟的红孩,及其兄弟它爸 三者去判断型号
                        if (father->leftchild == sibling)//L
                        {
                            if (sibling->leftchild != NULL && sibling->leftchild->color == RED)//LL
                            {

                                //旋转+变色
                                //变色规则:LL/RR型号:r变s,s变p,p变黑
                                //          LR/RL型号:r变p,p变黑
                                redchild->color = sibling->color;
                                sibling->color = father->color;
                                father->color = BLACK;

                                //旋转规则不变,但是时刻注意旋转传进的参数,是旋转节点的孙子辈
                                //LL型需要单右旋
                                RBNode* ptr = Right_Rotate(redchild);
                                if (grandfather != NULL)//它爷爷指针不为NULL,则用爷爷节点来抓住ptr
                                    if (ptr->val > grandfather->val)
                                        grandfather->rightchild = ptr;
                                    else
                                        grandfather->leftchild = ptr;
                                else//它爷爷指针为NULL,则用辅助节点来接收ptr
                                    pTree->root = ptr;

                                ptr->parent = grandfather;
                                return;
                            }
                            else//LR
                            {
                                //旋转+变色
                                //变色规则:LL/RR型号:r变s,s变p,p变黑
                                //          LR/RL型号:r变p,p变黑

                                redchild->color = father->color;
                                father->color = BLACK;

                                //LR型号,怎么旋转先左旋,再右旋

                                //第一步:先左旋(手动用代码实现,不能直接调用Left_Rotate)
                                sibling->rightchild = redchild->leftchild;//用sibling的右手来接收red_child的有可能存在的左孩
                                if (sibling->rightchild != NULL)//Sibling的右手真的接收到了一个孩子节点
                                {
                                    sibling->rightchild->parent = sibling;
                                }
                                redchild->leftchild = sibling;
                                sibling->parent = redchild;
                                father->leftchild = redchild;
                                redchild->parent = father;

                                //第二步:再右旋
                                RBNode* ptr = Right_Rotate(sibling);
                                if (grandfather != NULL)//它爷爷指针不为NULL,则用爷爷节点来抓住ptr
                                    if (ptr->val > grandfather->val)
                                        grandfather->rightchild = ptr;
                                    else
                                        grandfather->leftchild = ptr;
                                else//它爷爷指针为NULL,则用辅助节点来接收ptr
                                    pTree->root = ptr;

                                ptr->parent = grandfather;
                                return;
                            }
                        }
                        else//R
                        {
                            if (sibling->rightchild != NULL && sibling->rightchild->color == RED)//RR
                            {
                                //注意:如果这种情况sibling有两个红孩,redchild此时指向的是左边的红孩,给redchild修正一下,让其指向右侧的红孩
                                redchild = sibling->rightchild;

                                //旋转+变色
                                //变色规则:LL/RR型号:r变s,s变p,p变黑
                                //          LR/RL型号:r变p,p变黑
                                redchild->color = sibling->color;
                                sibling->color = father->color;
                                father->color = BLACK;

                                //旋转规则不变,但是时刻注意旋转传进的参数,是旋转节点的孙子辈
                                //RR型需要单左旋
                                RBNode* ptr = Left_Rotate(redchild);
                                if (grandfather != NULL)//它爷爷指针不为NULL,则用爷爷节点来抓住ptr
                                    if (ptr->val > grandfather->val)
                                        grandfather->rightchild = ptr;
                                    else
                                        grandfather->leftchild = ptr;
                                else//它爷爷指针为NULL,则用辅助节点来接收ptr
                                    pTree->root = ptr;

                                ptr->parent = grandfather;
                                return;
                            }
                            else//RL
                            {
                                //旋转+变色
                                //变色规则:LL/RR型号:r变s,s变p,p变黑
                                //          LR/RL型号:r变p,p变黑

                                redchild->color = father->color;
                                father->color = BLACK;

                                //RL型号,怎么旋转先右旋,再左旋
                                //第一步:先右旋(手动用代码实现,不能直接调用Right_Rotate)
                                sibling->leftchild = redchild->rightchild;//用sibling的左手来接收red_child的有可能存在的右孩
                                if (sibling->leftchild != NULL)//Sibling的右手真的接收到了一个孩子节点
                                {
                                    sibling->leftchild->parent = sibling;
                                }
                                redchild->rightchild = sibling;
                                sibling->parent = redchild;
                                father->rightchild = redchild;
                                redchild->parent = father;

                                //第二步:再左旋
                                RBNode* ptr = Left_Rotate(sibling);
                                if (grandfather != NULL)//它爷爷指针不为NULL,则用爷爷节点来抓住ptr
                                    if (ptr->val > grandfather->val)
                                        grandfather->rightchild = ptr;
                                    else
                                        grandfather->leftchild = ptr;
                                else//它爷爷指针为NULL,则用辅助节点来接收ptr
                                    pTree->root = ptr;

                                ptr->parent = grandfather;
                                return;
                            }
                        }
                        return;
                    }
                    //4.5 如果此时Node指向的节点非根节点,且颜色是黑色,且兄弟是黑色,且兄弟都是黑孩 --> 兄弟变红,双黑上移
                    else
                    {
                        sibling->color = RED;
                        //双黑上移,遇见的是红色节点或根节点 ->  直接变黑,结束
                        if (father->color == RED || father->parent == NULL)
                        {
                            father->color = BLACK;
                            return;
                        }
                        //双黑上移,遇见的是黑色节点,此时可以确定的是它一定有兄弟
                        //此时根据其兄弟的情况,继续判断即可
                        Delete_Adjust_Rotate(pTree, father, false);//这里的第二个参数father是不能删除的,因为删除的Node已将被被处理掉了
                        return;
                    }
                }
            }
        }
    }
}
  • tag 参数区分是否真删除节点
  • 完整处理所有边界
  • 全程维护 parent 指针
  • 递归向上调整双黑

七、初始化+插入+删除+查找+打印(递归+非递归)

cpp 复制代码
//1.初始化
void Init_RBTree(RBTree* pTree)
{
    //0.asssert
    pTree->root = NULL;
}

//2.插入
bool Insert_RBTree(RBTree* pTree, ELEMTYPE val)
{
    //0.assert
    //0.5 购买新节点
    RBNode* pnewnode = BuyNode();
    pnewnode->val = val;

    //1.申请两个变量p和pp,用来在红黑树里向下遍历,用来找到val值节点的插入位置
    RBNode* p = pTree->root;
    RBNode* pp = NULL;

    if (p == NULL)//它是空树
    {
        pTree->root = pnewnode;
        pnewnode->color = BLACK;
        return true;
    }

    //2.进入循环,循环条件是p指向节点存在,且其值不是我所需要的
    while (p != NULL && p->val != val)
    {
        //3.根据和p的有效值的比较,让指针p走自己的左侧或者右侧去遍历
        pp = p;

        p = val < p->val ? p->leftchild : p->rightchild;
    }


    //4.当while循环结束的时候,可能性1:p指向的节点值不存在,则准备插入pnewnode
                             //可能性2:p指向的节点,存在,不用插入pnewnode
    if (p != NULL)
    {
        free(pnewnode);
        return true;
    }
    else//可能性1:p指向的节点值不存在,则准备插入pnewnode
    {
        if (val < pp->val)
        {
            pp->leftchild = pnewnode;
        }
        else
        {
            pp->rightchild = pnewnode;
        }
        pnewnode->parent = pp;
    }

    if (pp->color == RED)//则违反"不红红"
    {
        Insert_Adjust_Rotate(pTree, pnewnode);
    }
    //5.结束
    return true;

}


//3.删除 //洗菜切菜
bool Delete_RBTree(RBTree* pTree, ELEMTYPE val)
{
    //0.assert  判断辅助节点存在与否

    //1.判空   判断红黑树是否存在有效节点
    if (pTree->root == NULL)
        return true;

    //2.找到待删除节点
    RBNode* p = Search_RBTree(pTree, val);
    if (p == NULL)
        return true;


    //3.判断此时待删除节点节点,什么情况
    //删除节点,会存在3种情况:两个孩子/一个孩子/零个孩子
    //4.待删除节点有两个孩子 --> 直接前驱/直接后继代替后,
    // 替代后的节点就变成一个孩子情况或者零个孩子情况
    if (p->leftchild != NULL && p->rightchild != NULL)
    {
        RBNode* cat = p->rightchild;
        while (cat->leftchild != NULL)
            cat = cat->leftchild;

        p->val = cat->val;
        p = cat;
    }


    //代码指向到这里,此时的p一定指向的是单分支/零分支情况
    //5.直接调用删除调整函数即可
    Delete_Adjust_Rotate(pTree, p, true);

    return true;
}

//4.查询
RBNode* Search_RBTree(RBTree* pTree, ELEMTYPE val)
{
    RBNode* p = pTree->root;

    while (p != NULL && p->val != val)
        p = val < p->val ? p->leftchild : p->rightchild;

    return p;
}

//5.打印(用中序遍历打印)
void Show_inOrder(RBNode* root)//递归
{
    if (root == NULL)
        return;

    Show_inOrder(root->leftchild);
    printf("%d ", root->val);
    Show_inOrder(root->rightchild);
}
#include <stack>
void Show_inOrder2(RBTree* pTree)//非递归
{
    assert(pTree != NULL);
    if (pTree->root == NULL) return;

    std::stack<RBNode*> st;
    RBNode* p = pTree->root;

    while (p != NULL || !st.empty())
    {
        // 一路左走入栈
        while (p != NULL)
        {
            st.push(p);
            p = p->leftchild;
        }

        // 出栈访问
        p = st.top();
        st.pop();
        printf("%d ", p->val);

        // 右子树
        p = p->rightchild;
    }
}
int main()
{
    RBTree head;
    Init_RBTree(&head);

    Insert_RBTree(&head, 17);
    Insert_RBTree(&head, 18);
    Insert_RBTree(&head, 23);
    Insert_RBTree(&head, 34);
    Insert_RBTree(&head, 27);
    Insert_RBTree(&head, 15);
    Insert_RBTree(&head, 9);
    Insert_RBTree(&head, 6);
    Insert_RBTree(&head, 8);
    Insert_RBTree(&head, 5);
    Insert_RBTree(&head, 25);
    Show_inOrder(head.root);
    printf("\n");

    Delete_RBTree(&head, 18);
    Delete_RBTree(&head, 9);
    Delete_RBTree(&head, 34);

    //Delete_RBTree(&head, 25);
    //Delete_RBTree(&head, 15);
    //Delete_RBTree(&head, 6);
    //Delete_RBTree(&head, 27);
    //Delete_RBTree(&head, 17);
    //Delete_RBTree(&head, 5);
    //Delete_RBTree(&head, 23);
    //Delete_RBTree(&head, 8);
    Show_inOrder(head.root);
    printf("\n");
    Show_inOrder2(&head);

    return 0;
}

运行结果:


八、红黑树高频15 问(背会直接拿分)

  1. **红黑树是什么?**弱平衡二叉搜索树,用颜色维持平衡。
  2. **红黑树 5 大性质?**根黑、叶黑、不红红、黑同长。
  3. **新节点为什么是红色?**减少黑色节点变化,降低调整成本。
  4. 插入最坏旋转几次? 最多 2 次
  5. 删除最坏旋转几次? 最多 3 次
  6. **AVL 和 红黑树 区别?**AVL 严格平衡查询快;红黑弱平衡插入删除快。
  7. **什么是双黑?**删除黑色节点产生的虚拟黑色。
  8. 红黑树时间复杂度? 严格 O(logn)
  9. **哪里用红黑树?**C++ STL map/set、Java TreeMap、Linux 内核。
  10. **红红冲突怎么解决?**看叔叔,红变色,黑旋转。
  11. **左旋和右旋区别?**处理 RR 型和 LL 型失衡。
  12. **删除红色节点需要调整吗?**不需要。
  13. **删除黑色节点需要调整吗?**需要,处理双黑。
  14. **红黑树是完全二叉树吗?**不是,是弱平衡二叉树。
  15. 红黑树高度? 最多 2log(n+1)

九、总结(最核心口诀)

**红黑树五定律:**根黑、叶黑、不红红、黑路等长。

**插入:**新点红,父黑停,父红看叔情。叔红变色,叔黑旋形。

**删除:**删红无事,删黑双惊。兄红旋,兄黑看侄,双黑上行。

旋转少、效率高、STL 底层离不了!

相关推荐
bucenggaibian2 小时前
C语言如何直接控制硬件?指针、内存与寄存器
c语言·内存·指针·寄存器·硬件控制
Lazionr2 小时前
【链表经典OJ-下】
c语言·数据结构·链表
CPUOS20102 小时前
嵌入式C语言高级编程之接口隔离原则
c语言·网络·接口隔离原则
sghuter2 小时前
HTML头部元信息避坑指南
c语言·前端·html·cocoa
a里啊里啊2 小时前
软考-软件评测师:知识点整理(六)——数据结构与算法
数据结构·算法·链表·软考·软件评测师
想带你从多云到转晴2 小时前
06、数据结构与算法---二叉树
java·数据结构·算法
酉鬼女又兒2 小时前
Leetcode 26.删除有序数组中的重复项 双指针巧解有序数组去重:从快慢指针到原地修改算法的精髓
java·数据结构·算法·leetcode·职场和发展·蓝桥杯·排序算法
光电笑映2 小时前
Linux C/C++ 开发工具(下):make/Makefile、进度条小程序与 gdb 调试器
linux·c语言·c++