首页 > 其他分享 >红黑树的实现

红黑树的实现

时间:2024-09-10 16:13:42浏览次数:9  
标签:node parent 实现 Color Black 红黑树 节点 Red

上一篇中我们讲述了红黑树的插入, 以及删除时需要进行的各种调整的情况, 根据这些情况, 我们可以用代码实现红黑树的插入与删除操作.

节点的定义

一颗红黑树的定义如下:

// 定义颜色枚举类型
enum Color { RED, BLACK };

template <class T>
struct Red_Black_Node {
    Red_Black_Node* left;
    Red_Black_Node* right;
    Red_Black_Node* parent;
    //记录坐标, 用于打印
    int x, y;
    T value;                    // 节点的值, 红黑树中节点的值是必须可以改变的
    Color node_color;           // 节点的颜色
    bool NIL;                   // 该节点是否是 NIL 节点
    Red_Black_Node(T value, Color node_color, bool NIL);    // 构造函数
    void Set_Color(Color new_color);
    void SetValue(T new_value);
    // 将当前节点的父节点指向新的对应的孩子节点 new_child
    void SetNewChild(Red_Black_Node* new_child);
    Red_Black_Node* LeftRotate();
    Red_Black_Node* RightRotate();
    Red_Black_Node* GetUncle();
    Red_Black_Node* GetSibling();
    Red_Black_Node* GetCloseNephew();
    Red_Black_Node* GetFarNephew();
};

为了在删除操作中简单化, 我们在节点的旋转与调整中引入了一些额外的操作, 例如 SetNewChild 函数, 表示替换当前节点的父节点的孩子节点为新的节点, 在后续删除当前节点的时候十分有用, 实现如下:

template <class T>
void Red_Black_Node<T>::SetNewChild(Red_Black_Node* new_child) {
    if (this->parent == nullptr) {
        return;
    }
    if (this == this->parent->left) {
        this->parent->left = new_child;
    }
    else {
        this->parent->right = new_child;
    }
    new_child->parent = this->parent;
}

还有, 在旋转的操作中, 我们也将旋转后的根节点的与父节点进行了连接, 实现如下:

// 以当前节点为根节点进行左旋转
template <class T>
Red_Black_Node<T>* Red_Black_Node<T>::LeftRotate() {
    Red_Black_Node* temp_parent = this->parent;
    Red_Black_Node* temp = this->right;
    this->right->left->parent = this;
    this->right = this->right->left;
    this->parent = temp;
    temp->left = this;
    if (temp_parent != nullptr) {
        if (this == temp_parent->left) {
            temp_parent->left = temp;
        }
        else {
            temp_parent->right = temp;
        }
        temp->parent = temp_parent;
    }
    else {
        temp->parent = nullptr;
    }
    return temp;
}

这些操作极大的简化了后续插入与删除的过程中的调整操作.

红黑树的插入操作

红黑树也是二叉查找树的一种, 其插入的开始步骤与普通的二叉查找树相同, 只有在插入这个节点之后, 才需要对树的结构进行调整, 插入操作的整体步骤如下:

template <class T>
void Red_Black_Tree<T>::insert(T value) {
    Red_Black_Node<T>* direct = this->root;
    Red_Black_Node<T>* pre_direct = this->root;
    // 找到插入的位置
    while (direct != nullptr && direct->value != value && !direct->NIL) {
        pre_direct = direct;
        if (direct->value < value) {
            direct = direct->right;
        }
        else {
            direct = direct->left;
        }
    }
    // 新建插入的节点
    Red_Black_Node<T>* new_node = new Red_Black_Node<T>(value, Color::RED, false);
    // 如果是空树, 设置新建节点为根节点
    if (this->root == nullptr) {
        // 根节点为黑色
        new_node->Set_Color(Color::BLACK);
        this->root = new_node;
        Red_Black_Node<T>* new_left_node = new Red_Black_Node<T>(0, Color::BLACK, true);
        Red_Black_Node<T>* new_right_node = new Red_Black_Node<T>(0, Color::BLACK, true);
        this->root->left = new_left_node;
        this->root->right = new_right_node;
        return;
    }
    // 如果这个节点不是NIL节点, 该值已存在
    if (!direct->NIL) {
        return;
    }
    // 在正确的位置插入新建的节点
    if (direct == pre_direct->left) {
        Red_Black_Node<T>* new_right_node = new Red_Black_Node<T>(0, Color::BLACK, true);
        new_node->left = pre_direct->left;
        new_node->right = new_right_node;
        pre_direct->left = new_node;
    }
    else {
        Red_Black_Node<T>* new_left_node = new Red_Black_Node<T>(0, Color::BLACK, true);
        new_node->right = pre_direct->right;
        new_node->left = new_left_node;
        pre_direct->right = new_node;
    }
    new_node->parent = pre_direct;
    // 插入节点之后调整红黑树的结构
    InsertBalanceAdjust(new_node);
}

红黑树结构的调整

将一个节点插入到红黑树中, 需要对树的结构进行调整, 按照 上一篇博客 所示, 我们列出了各种不同的情况下需要进行的调整操作, 这些情况如下:

template <class T>
int Red_Black_Tree<T>::InsertBalanceAction(Red_Black_Node<T>* node) {
    // 走到根节点了, 将该节点设为根节点, 为黑色
    if (node->parent == nullptr) {
        return 0;
    }
    // 如果当前节点的父节点存在并且为黑色
    if (node->parent->node_color == Color::BLACK) {
        return 1;
    }
    // 如果父节点是红色
    if (node->parent->node_color == Color::RED) {
        // 因为父亲节点是红色节点, 就一定不是根节点, 所以当前节点一定存在叔叔节点
        // 如果叔叔节点为红色
        if (node->GetUncle()->node_color == Color::RED) {
            return 2;
        }
        else {
            // 如果叔叔节点为黑色, 需要判断该节点的旋转方向
            if (node == node->parent->left && node->parent == node->parent->parent->left) {
                return 3;
            }
            else if (node == node->parent->right && node->parent == node->parent->parent->right) {
                return 4;
            }
            else if (node == node->parent->right && node->parent == node->parent->parent->left) {
                return 5;
            }
            else if (node == node->parent->left && node->parent == node->parent->parent->right) {
                return 6;
            }
            return -1;
        }
    }
    return -1;
}

然后是我们后续进行调整的步骤, 我们列出每种调整步骤如下:

template <class T>
void Red_Black_Tree<T>::InsertBalanceAdjust(Red_Black_Node<T>* node) {
    Red_Black_Node<T>* temp_node = nullptr;
    switch (this->InsertBalanceAction(node))
    {
    case 0:
        node->Set_Color(Color::BLACK);
        break;
    case 1:
        node->Set_Color(Color::RED);
        break;
    case 2:
        node->Set_Color(Color::RED);
        node->parent->Set_Color(Color::BLACK);
        node->GetUncle()->Set_Color(Color::BLACK);
        InsertBalanceAdjust(node->parent->parent);
        break;
    case 3:
        node->Set_Color(Color::RED);
        node->parent->Set_Color(Color::BLACK);
        node->parent->parent->Set_Color(Color::RED);
        temp_node = node->parent->parent->RightRotate();
        if (temp_node->parent == nullptr) {
            this->root = temp_node;
        }
        break;
    case 4:
        node->Set_Color(Color::RED);
        node->parent->Set_Color(Color::BLACK);
        node->parent->parent->Set_Color(Color::RED);
        temp_node = node->parent->parent->LeftRotate();
        if (temp_node->parent == nullptr) {
            this->root = temp_node;
        }
        break;
    case 5:
        node->Set_Color(Color::RED);
        // node->parent->parent->Set_Color(Color::RED);
        node->parent->parent->left = node->parent->LeftRotate();
        // 实际问题被转换为case3 解决
        InsertBalanceAdjust(node->left);
        break;
    case 6:
        node->Set_Color(Color::RED);
        node->parent->parent->Set_Color(Color::RED);
        node->parent->parent->right = node->parent->RightRotate();
        // 实际问题被转换为case4 解决
        InsertBalanceAdjust(node->right);
    default:
        break;
    }
}

请注意在 case5case6中我们进行了转换, 但是转换之后, 需要调整的节点不是新插入的节点, 而是插入节点的孩子节点.

标签:node,parent,实现,Color,Black,红黑树,节点,Red
From: https://www.cnblogs.com/wevolf/p/18406622

相关文章

  • 【useTranslation】兼容数组解构和对象解构的三种实现方式
    useTranslation使用:数组解构:const[t,i18n]=useTranslation();对象解构:const{t,i18n}=useTranslation();useTranslation兼容数组解构和对象解构的三种实现方式:1.返回带属性的数组在这种实现方式中,返回一个数组,并为该数组添加对象属性。这样可以同时使用数组......
  • ubuntu20.04 Qt6引用dcmtk库实现dicom文件读取和字符集转换
    1环境问题安装完Qt6,新建Qt/QtQuickCMake工程编译出现如下错误:Foundpackageconfigurationfile:Qt6Config.cmakebutitsetQt6FOUNDtoFALSEsopackage"Qt6"isconsideredtobeNOTFOUND.原因:这是因为系统中缺少OpenGL库,可以安装libgl1-mesa-dev解决方法:su......
  • python装饰器模式实现切面功能
    引言在软件开发中,我们经常会遇到一些横切关注点(cross-cuttingconcerns),如日志记录、事务管理、安全性检查等,这些关注点通常会跨越多个模块。传统的编程方式会导致代码的重复和分散,难以维护。面向切面编程(AOP)是一种编程范式,它通过提供一种新的方式来模块化横切关注点,从而提高代码......
  • 基于Python的资产管理系统的设计与实现-附源码201117
    摘 要现代企业管理越来越强调利用有形资产来提供优质服务的能力,即通过资产管理来确保有形资产物尽其用、安全运行,在希望的时间和地点提供需要的设备,同时尽可能地降低运行和维护成本。资产管理系统为企业提供全面、迅速的资产信息,方便管理者了解和操作企业内部的资产管理。......
  • 基于ssm的校园拼车服务系统的设计与实现-附源码211633
    摘 要科技进步的飞速发展引起人们日常生活的巨大变化,电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流,人类发展的历史正进入一个新时代。在现实运用中,应用软件的工作规则和开发步骤,采用Java技术建设校......
  • Prometheus+Grafana+Alertmanager实现监控系统
    1.介绍监控系统有一下部分组成:prometheus服务(接收所有exporter发送过来的消息,配置告警规则;默认端口:9090)grafana可视化界面(将prometheus数据展示出来;默认端口:3000)exporter代理服务(将server上报的数据转换为prometheus能看懂的格式;nginx-vts默认端口:9913;nginx默认端口:9113)serve......
  • LG AI 研究中心开源 EXAONEPath:通过285M Patch级预训练模型变革组织病理学图像分析,实
    基于LGAIResearch在AI语言模型方面的显著成就,特别是推出EXAONE3.0之后,EXAONEPath的开发代表了另一个重要的里程碑。这标志着EXAONE在数字病理学这一关键医学诊断领域的一次重大转型,通过解决全幻灯片图像(WSI)在病理学中的复杂挑战以及提高病理图像处理效率,EXAONEPath广泛应用......
  • Redis实现延迟任务的操作流程
    延迟任务(DelayedTask)是指在未来的某个时间点,执行相应的任务。也就是说,延迟任务是一种计划任务,它被安排在特定的时间后执行,而不是立即执行。延迟任务的常见使用场景有以下几个:定时发送通知或消息:发送定时短信、邮件或应用内消息,如注册确认、订单状态更新、促销活动通知等......
  • Gemini实现游戏串流功能
    一、部署GeminiGemini使用教程-迅捷网络[来送福利]-博客园(cnblogs.com)二、部署Moonlight过程大概说一下,网上有太多太多moonlight的东西了需要运行游戏的机器上安装GFE(GeForceExperience),登录并开启GAMESTREAM(游戏串流)功能 注:这里有个坑起初我想的是,直接在办公......
  • jmeter通过beanshell中脚本实现随机获取某天(“yyyy-MM-dd HH:mm:ss“)前1周,一个月,一
    在接口测试中,请求参数中涉及时间的参数可能不是固定死的,因此jmeter想通过beanshell中脚本实现随机获取某天(statusTimeEnd(“yyyy-MM-ddHH:mm:ss”))前1周,一个月,一个季度,半年的时间0点,其中statusTimeEnd的值在用户参数中已配置。参考JMeter性能测试实战的方法:http://lit......