红黑树的实现

news/2025/1/15 18:09:49/文章来源:https://www.cnblogs.com/wevolf/p/18406622

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

节点的定义

一颗红黑树的定义如下:

// 定义颜色枚举类型
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_childvoid 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中我们进行了转换, 但是转换之后, 需要调整的节点不是新插入的节点, 而是插入节点的孩子节点.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/795010.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

搭建企业内部的大语言模型系统

大纲开源大语言模型 大语言模型管理 私有大语言模型服务部署方案开源大语言模型 担心安全与隐私?可私有部署的开源大模型商业大模型,不支持私有部署ChatGPT Claude Google Gemini 百度问心一言开源大模型,支持私有部署Mistral Meta Llama ChatGLM 阿里通义千问常用开源大模型…

115. 不同的子序列(leetcode)

https://leetcode.cn/problems/distinct-subsequences/submissions/563375885/ 这题比较有难度,具体不太好想到,需要以是否选择s[i]来划分子集这位描述的很清楚,不做过多赘述 class Solution {public int numDistinct(String s, String t) {// f[i][j]表示s中前i个字符中选择,有…

0.1+0.2 != 0.3 (Java为例)

1. 小数的二进制表示 以10.625为例。整数部分进行除2取余的操作,10的二进制为1010。小数部分进行乘2取整操作,直到小数部分为0或达到需要的精度:0.625*2=1.25 取整数1,小数部分0.25继续计算 0.25*2=0.5 取整数0,小数部分0.5继续计算 0.5*2=1.0 取整数1,小数部分为0,停止…

信创领域认证,来自工信部人才交流中心的PostgreSQL培训班

在国家大力发展信创软件和数据库行业的背景下,PostgreSQL 具有多方面的优势和机遇,具体体现在以下几个方面: 1. 技术优势契合信创需求: PostgreSQL 数据库是一个功能强大、性能稳定、可扩展性强的开源对象关系数据库系统,支持多种数据类型(如数组、JSON、XML 等),方便存储…

Salesforce职业规划:原厂,甲方,乙方,从业者应该如何选择?

Salesforce生态系统蓬勃发展,对不同角色的需求量不断增加。需求方包括使用Salesforce的最终用户(甲方)、实施Salesforce的咨询公司、为Salesforce创建应用程序的AppExchange公司(或ISV),当然还有Salesforce原厂。 Salesforce最终用户(甲方) 2020年,Salesforce的客户数…

3SRB5016-ASEMI三相整流桥3SRB5016

3SRB5016-ASEMI三相整流桥3SRB5016编辑:ll 3SRB5016-ASEMI三相整流桥3SRB5016 型号:3SRB5016 品牌:ASEMI 封装:3SRB-5 批号:2024+ 现货:50000+ 最大重复峰值反向电压:1600V 最大正向平均整流电流(Vdss):50A 功率(Pd):大功率 芯片个数:5 引脚数量:5 安装方式:直插 类…

34-样式迁移

类似于加了一层滤镜基于CNN的样式迁移:如下对于合成图片X,我们希望它的内容和输入的内容图片,放入同一个CNN,在某一个卷积层上,输出的与内容有关的特征能够匹配 同时,,对于样式图片,我们希望合成图片X,和样式图片放入同一个CNN,在某一个卷积层上,输出的与样式有关的…

STM32-ADC外设

1.通道 .规则通道 .注入通道 2.规则序列寄存器 配置通道的采样顺序 3.ADC周期4.ADC转换方式 *单次转换:adc每次只采集某个通道的一个点,如果需要再次采集,就需要重新使能。 *连续转换:adc采集某个通道一个点,转换完成后,再采集第二点。依次类推 4.扫描模式 *单次扫描模式…

SignalR跨域问题解决

本文来自博客园,作者:WantRemake,转载请注明原文链接:https://www.cnblogs.com/SmallChen/p/18406437

字符串类

常用类String基础知识String类的特性String类是一个final类,不能被继承 String类底层是一个final修饰的字符数组,表示不可变的字符序列(final char value[ ]) String的不可变性:当String值改变时,会在常量池中创建新的字符串字符串-创建字面量方式创建 String s1="a…

AI答案之书解来为你解决难题

本文由 ChatMoney团队出品介绍说明 “答案之书智能体”是您贴心的智慧伙伴,随时准备为您解答生活中的种种困惑。无论您在工作中遭遇瓶颈,还是在情感世界里迷失方向,亦或是对个人成长感到迷茫,它都能倾听您的心声,并给予准确且富有启发的回应。 它并非简单地给出答案,而是…

解锁生活密码,AI答案之书解决复杂难题

本文由 ChatMoney团队出品介绍说明 “答案之书智能体”是您贴心的智慧伙伴,随时准备为您解答生活中的种种困惑。无论您在工作中遭遇瓶颈,还是在情感世界里迷失方向,亦或是对个人成长感到迷茫,它都能倾听您的心声,并给予准确且富有启发的回应。 它并非简单地给出答案,而是…