红黑树模拟实现

目录

概念

性质

节点定义

红黑树的插入

完整代码


概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

下面就是一个红黑树: 

注:从根节点到NIL节点才是才算是一条路径。

性质

1.每个结点不是红色就是黑色

2.根节点是黑色的
3.如果一个节点是红色的,则它的两个孩子结点是黑色的
4.对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点5.每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

节点定义
enum Colour
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Colour _col;RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED){}
};

 通过上面的代码不难发现,我们将新创建的节点设置为红色,为什么不设置成黑色呢?

根据红黑树的性质4,我们知道,每条路径上黑色节点的数目是相等的,倘若我们在插入节点时,插入的是黑色节点,那么将影响整棵树;插入的是红色节点,不至于影响整棵树,因为如果新插入节点的父节点是黑色,将不需要进行调整;如果新插入节点的父节点是红色,只需要进行相关调整即可。

总的来说,新插入节点是红色,影响范围更小,利于编码实现,更方便。

红黑树的插入

1.按照二叉搜索树的规则插入节点;

2.判断红黑树的结构是否被破坏。

因为新节点的默认颜色是红色,因此:如果其父节点是黑色,就没有违反红黑树任何行者,则不需要调整;但当新插入节点的父节点是红色时,就违反了性质三(不能出现连续的红色节点),此时需要对红黑树分情况来讨论:

【约定】:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点。

情况一:cur为红,p为红,g为黑,u存在且为红

解决方式:将p,u改为黑,g改为红,然后把g改为cur,继续向上调整。

情况二:cur为红,p为红,g为黑,u不存在 / u存在且为黑

解决方式:p为g的左孩子,cur为p的左孩子,则进行右单旋,p改为黑色,g改为红色;

                  p为g的右孩子,cur为p的右孩子,则进行左单旋,p改为黑色,g改为红色。

此时u有两种情况:

1.u节点不存在,此时cur一定是新增节点,因为如果cur不是新插入节点,则cur和p一定有一个节点的颜色是黑色的,就不满足性质四(每条路径黑色节点个数相同)。

2.u节点存在,则其一定是黑色的.

情况三:cur为红,p为红,g为黑,u不存在 / u存在且为黑

解决方式:p为g的左孩子,cur为p的右孩子,则进行左单旋,再右单旋,  p改为黑色,g改为红色;

                  p为g的右孩子,cur为p的左孩子,则进行右单旋,再左单旋,  p改为黑色,g改为红色。

此时u有两种情况:

1.u节点不存在,此时cur一定是新增节点,因为如果cur不是新插入节点,则cur和p一定有一个节点的颜色是黑色的,就不满足性质四(每条路径黑色节点个数相同)。

2.u节点存在,则其一定是黑色的.

插入代码实现 

bool Insert(const pair<K, V>& kv)
{if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}// 新增节点给红色cur = new Node(kv);cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){//     g//   p   u// cNode* uncle = grandfather->_right;if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上更新处理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_left){// 单旋//     g//   p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// 双旋//     g//   p//     cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else  // parent == grandfather->_right{//     g//   u   p //          c//Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上处理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//   u   p //     c//RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;
}
完整代码
#pragma onceenum Colour
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Colour _col;RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED){}
};template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}// 新增节点给红色cur = new Node(kv);cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){//     g//   p   u// cNode* uncle = grandfather->_right;if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上更新处理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_left){// 单旋//     g//   p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// 双旋//     g//   p//     cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else  // parent == grandfather->_right{//     g//   u   p //          c//Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上处理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//   u   p //     c//RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;Node* parentParent = parent->_parent;parent->_parent = subR;if (subRL)subRL->_parent = parent;if (_root == parent){_root = subR;subR->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;if (_root == parent){_root = subL;subL->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}}void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}// 根节点->当前节点这条路径的黑色节点的数量bool Check(Node* root, int blacknum, const int refVal){if (root == nullptr){//cout << balcknum << endl;if (blacknum != refVal){cout << "存在黑色节点数量不相等的路径" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << "有连续的红色节点" << endl;return false;}if (root->_col == BLACK){++blacknum;}return Check(root->_left, blacknum, refVal)&& Check(root->_right, blacknum, refVal);}bool IsBalance(){if (_root == nullptr)return true;if (_root->_col == RED)return false;//参考值int refVal = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){++refVal;}cur = cur->_left;}int blacknum = 0;return Check(_root, blacknum, refVal);}private:Node* _root = nullptr;
};

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

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

相关文章

充电桩开源平台,开发流程有图有工具

慧哥充电桩开源平台产品研发流程是确保产品从概念阶段到市场推广阶段的有序进行的关键。以下是对您给出的步骤的详细解释和建议&#xff1a; 设计业务流程: 在这一步&#xff0c;团队需要确定产品的核心功能、目标用户以及如何满足用户需求。进行市场调研&#xff0c;了解竞争…

PostMan Error:Maximum response size reached

一、问题描述 用postman本地测试&#xff0c;restful api接口导出文件&#xff0c;文件大小为190M&#xff0c;服务没问题&#xff0c;总是在导出时&#xff0c;抛出&#xff1a;Error:Maximum response size reached。开始以为是服务相应文件过大或者相应时间超时导致的。其实…

双向链表 -- 详细理解和实现

欢迎光顾我的homepage 前言 双向链表是一种带头双向循环的链表。在双向链表中&#xff0c;首先存在着一个头结点&#xff1b;其次每个节点有指向下一个节点的指针next 和指向上一个节点的指针prev &#xff1b…

Trimble realworks 2024.02 中文激活版获取License下载软件

Trimble realworks 2024 是领先的3D点云和2D图像处理解决方案&#xff0c;使用可您提供了一组用于处理的工具&#xff0c;以便为您的应用程序&#xff08;或项目&#xff09;获取必要的信息。此处理可以分为三种模式&#xff0c;在注册中&#xff0c;您可以注册相对于其他扫描和…

通信协议_Modbus协议简介

概念介绍 Modbus协议&#xff1a;一种串行通信协议&#xff0c;是Modicon公司&#xff08;现在的施耐德电气Schneider Electric&#xff09;于1979年为使用可编程逻辑控制器&#xff08;PLC&#xff09;通信而发表。Modbus已经成为工业领域通信协议的业界标准&#xff08;De f…

大舍传媒:如何在海外新闻媒体发稿报道摩洛哥?

引言 作为媒体行业的专家&#xff0c;我将分享一些关于在海外新闻媒体发稿报道摩洛哥的干货教程。本教程将带您深入了解三个重要的新闻媒体平台&#xff1a;Mediterranean News、Morocco News和North African News。 地中海Mediterranean News Mediterranean News是一个知名…

合合信息大模型“加速器”重磅上线

大模型技术的发展和应用&#xff0c;预示着更加智能化、个性化未来的到来。如果将大模型比喻为正在疾驰的科技列车&#xff0c;语料便是珍贵的“燃料”。本次世界人工智能大会期间&#xff0c;合合信息为大模型打造的“加速器”解决方案备受关注。 在大模型训练的上游阶段&…

【计算机毕业设计】021基于weixin小程序微信点餐

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Python学习中使用循环(for, while)

在Python编程语言中&#xff0c;循环是一个非常重要的概念&#xff0c;可以帮助我们在代码中重复执行某些操作。Python支持两种主要的循环结构&#xff1a;for 循环和 while 循环。 1. for 循环 for 循环用于遍历一个序列&#xff08;如列表、元组、字符串&#xff09;或其他…

Thingsboard 系列之通过 ESP8266+MQTT 模拟设备上报数据到平台

前置工作 Thingsboard平台ESP 8266 NodeMCU 开发板IDE&#xff1a; Arduino 或 VScode 均可 服务端具体对接流程 系统管理员账号通过 Thingsboard 控制面板创建租户等信息并以租户账号登录 实体 —> 设备维护具体设备信息 创建完成后通过管理凭据修改或直接复制访问令牌…

致远OA同步组织架构到企业微信

致远OA同步组织架构到企业微信 可适配任何系统 背景 原有的微协同无法满足人员同步&#xff0c;因为在启用微协同的时候&#xff0c;企业微信已经存在人员&#xff0c;所以配置微协同之后&#xff0c;人员会出现新增而不会同步修改 方案 重写同步&#xff0c;针对已经存在…

网安加·百家讲坛 | 关昕健:新时代企业数据安全运营思路

作者简介&#xff1a;关昕健&#xff0c;某运营商安全专家&#xff0c;2015年获CISSP认证&#xff0c;长期负责企业安全运营工作&#xff0c;关注国内外数据安全动态与解决方案&#xff0c;持续开展数据安全运营实践。 近年来&#xff0c;随着《数据安全法》的出台和国家数据局…

js逆向案例 | 加速乐反爬逆向

前言 加速乐作为一种常见的反爬虫技术&#xff0c;在网络上已有大量详尽深入的教程可供参考。然而&#xff0c;对于那些初次接触的人来说&#xff0c;直接面对它可能仍会感到困惑。 声明 本文仅用于学习交流&#xff0c;学习探讨逆向知识&#xff0c;欢迎私信共享学习心得。如…

【YOLOv5/v7改进系列】改进池化层为RFB

一、导言 论文 "Receptive Field Block Net for Accurate and Fast Object Detection" 中提出的 RFB (Receptive Field Block) 模块旨在模仿人类视觉系统中的感受野结构&#xff0c;以增强深度学习模型对不同尺度和位置的目标检测能力。下面总结了RFB模块的主要优点…

AIGC时代程序员的跃迁——编程高手的密码武器

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

一、redis-万字长文读懂redis

高性能分布式缓存Redis `第一篇章`1.1缓存发展史&缓存分类1.1.1 大型网站中缓存的使用带来的问题1.1.2 常见缓存的分类及对比与memcache对比1.2 数据类型选择&应用场景1.2.1 string1.2.2 hash1.2.3 链表1.2.4 set1.2.5 sortedset有序集合类型1.2.6 总结1.3 Redis高级应…

[数仓]三、离线数仓(Hive数仓系统)

第1章 数仓分层 1.1 为什么要分层 DIM&#xff1a;dimensionality 维度 1.2 数据集市与数据仓库概念 1.3 数仓命名规范 1.3.1 表命名 ODS层命名为ods_表名DIM层命名为dim_表名DWD层命名为dwd_表名DWS层命名为dws_表名 DWT层命名为dwt_表名ADS层命名为ads_表名临时表命名为…

昇思25天训练营Day11 - 基于 MindSpore 实现 BERT 对话情绪识别

模型简介 BERT全称是来自变换器的双向编码器表征量&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;&#xff0c;它是Google于2018年末开发并发布的一种新型语言模型。与BERT模型相似的预训练语言模型例如问答、命名实体识别、自然语言推理、…

56、最近邻向量量化(LVQ) 网络训练对输入向量进行分类

1、LVQ 网络训练对输入向量进行分类简介 1&#xff09;简介 LVQ&#xff08;最近邻向量量化&#xff09;是一种简单而有效的神经网络模型&#xff0c;用于对输入向量进行分类。LVQ网络通过学习一组原型向量&#xff08;也称为代码矢量或参考向量&#xff09;&#xff0c;来表…

SAP Build4-office 操作

1. 邮件操作 1.1 前期准备 商店中找到outlook的sdk&#xff0c;添加到build中 在process中添加outlook的SDK 电脑上装了outlook的邮箱并且已经登录 我用个人foxmail邮箱向outlook发了一封带附件的销售订单邮件&#xff0c;就以此作为例子 1.2 搜索邮件 搜索有两层&…