【C++】RBTree(红黑树)模拟实现
文章目录
- 1.红黑树的概念
- 2.红黑树的性质
- 3.红黑树的结点
- 4.insert函数(插入结点)
- 5.左旋、右旋
- 6.总代码
后续有时间会增加erase
1.红黑树的概念
红黑树是一种自平衡的二叉搜索树。每个节点额外存储了一个 color 字段 (“RED” or “BLACK”), 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡。
2.红黑树的性质
一棵合法的红黑树必须遵循以下四条性质:
- 节点为红色或黑色
- 根节点是黑色的 (在不同的实现下,该条性质并非必须满足)
- NIL 节点(空叶子节点)为黑色
- 红色节点的子节点为黑色
- 从根节点到 NIL节点的每条路径上的黑色节点数量相同
3.红黑树的结点
enum Colour
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;std::pair<K, V> _kv;Colour _col;RBTreeNode(const std::pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED){}
};
4.insert函数(插入结点)
新节点的默认颜色是红色(如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质4不能有连在一起的红色节点,此时需要对红黑树分情况来讨论)
约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点
所有插入的情况可分为以下三种:
- 情况一: cur为红,p为红,g为黑,u存在且为红
- 情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑
- 情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑
bool Insert(const std::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;}else{parent->_left = cur;}cur->_parent = parent;// parent的颜色是黑色也结束while (parent && parent->_col == RED){// 关键看叔叔Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* 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 u// c RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g // p u// c RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;// 叔叔存在且为红,-》变色即可if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上处理cur = grandfather;parent = cur->_parent;}else // 叔叔不存在,或者存在且为黑{// 情况二:叔叔不存在或者存在且为黑// 旋转+变色// g// u p// cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g// u p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}
5.左旋、右旋
这里和AVLTree的旋转一样
void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node* ppNode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node* ppNode = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;_root->_parent = nullptr;}else{if (ppNode->_right == parent){ppNode->_right = subR;}else{ppNode->_left = subR;}subR->_parent = ppNode;}}
6.总代码
#include<vector>enum Colour
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;std::pair<K, V> _kv;Colour _col;RBTreeNode(const std::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 std::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;}else{parent->_left = cur;}cur->_parent = parent;// parent的颜色是黑色也结束while (parent && parent->_col == RED){// 关键看叔叔Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* 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 u// c RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g // p u// c RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;// 叔叔存在且为红,-》变色即可if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上处理cur = grandfather;parent = cur->_parent;}else // 叔叔不存在,或者存在且为黑{// 情况二:叔叔不存在或者存在且为黑// 旋转+变色// g// u p// cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g// u p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node* ppNode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node* ppNode = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;_root->_parent = nullptr;}else{if (ppNode->_right == parent){ppNode->_right = subR;}else{ppNode->_left = subR;}subR->_parent = ppNode;}}void InOrder(){_InOrder(_root);cout << endl;}bool IsBalance(){if (_root->_col == RED){return false;}int refNum = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){++refNum;}cur = cur->_left;}return Check(_root, 0, refNum);}private:bool Check(Node* root, int blackNum, const int refNum){if (root == nullptr){//cout << blackNum << endl;if (refNum != blackNum){cout << "存在黑色节点的数量不相等的路径" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << root->_kv.first << "存在连续的红色节点" << endl;return false;}if (root->_col == BLACK){blackNum++;}return Check(root->_left, blackNum, refNum)&& Check(root->_right, blackNum, refNum);}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}private:Node* _root = nullptr;//size_t _size = 0;
};
相关文章:
【C++】RBTree(红黑树)模拟实现
文章目录 1.红黑树的概念2.红黑树的性质3.红黑树的结点4.insert函数(插入结点)5.左旋、右旋6.总代码 后续有时间会增加erase 1.红黑树的概念 红黑树是一种自平衡的二叉搜索树。每个节点额外存储了一个 color 字段 (“RED” or “BLACK”), …...
Redis——优惠券秒杀问题(分布式id、一人多单超卖、乐悲锁、CAS、分布式锁、Redisson)
#想cry 好想cry 目录 1 全局唯一id 1.1 自增ID存在的问题 1.2 分布式ID的需求 1.3 分布式ID的实现方式 1.4 自定义分布式ID生成器(示例) 1.5 总结 2 优惠券秒杀接口实现 3 单体系统下一人多单超卖问题及解决方案 3.1 问题背景 3.2 超卖问题的…...
【现代深度学习技术】深度学习计算 | GPU
【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈PyTorch深度学习 ⌋ ⌋ ⌋ 深度学习 (DL, Deep Learning) 特指基于深层神经网络模型和方法的机器学习。它是在统计机器学习、人工神经网络等算法模型基础上,结合当代大数据和大算力的发展而发展出来的。深度学习最重…...
USB Flash闪存驱动器安全分析(第一部分)
翻译原文链接:Hacking Some More Secure USB Flash Drives (Part I) | SySS Tech Blog 文章翻译总结:文章对一些具有AES硬件加密的USB闪存驱动器的网络安全分析研究。研究由SySS的IT安全专家Matthias Deeg进行,他在2022年初发现了几个安全漏…...
3.1 严格Stubbing模式
严格Stubbing(Strict Stubbing)是Mockito提供的一种增强测试严谨性的模式,旨在检测以下问题: 多余的Stubbing:配置了未被调用的方法桩。不必要的Stubbing:Stubbing未被使用且不影响测试结果。桩顺序错误&a…...
【Linux】--- 基础开发工具之yum/apt、vim、gcc/g++的使用
Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏: Linux网络编程 本篇博客我们来认识一下Linux中的一些基础开发工具 --- yum,vim,gcc/g。 🏠 yum 🎸 什么是yum 当用户想下载软…...
Python + WhisperX:解锁语音识别的高效新姿势
大家好,我是烤鸭: 最近在尝试做视频的质量分析,打算利用asr针对声音判断是否有人声,以及识别出来的文本进行进一步操作。asr看了几个开源的,最终选择了openai的whisper,后来发现性能不行,又换了…...
redis 缓存击穿问题与解决方案
前言1. 什么是缓存击穿?2. 如何解决缓存击穿?怎么做?方案1: 定时刷新方案2: 自动续期方案3: 定时续期 如何选? 前言 当我们使用redis做缓存的时候,查询流程一般是先查询redis,如果redis未命中,再查询MySQL,将MySQL查询的数据同步到redis(回源),最后返回数据 流程图 为什…...
SAP HCM 批量核算工资报错如何定位错误 (SAT分析错误)
导读 簇目录 (表 RGDIR) 不包含任何记录:今天遇到一个很奇怪的问题,簇目录 (表 RGDIR) 不包含任何记录,而且出现的问题没有具体到员工编号,所以处理问题非常棘手。今天分享下我的处理方式,以便大家遇到这类的问题不知道如何下手。…...
常用共轭先验分布
内容来源 贝叶斯统计(第二版)中国统计出版社 正态分布的均值 总体分布 N ( θ , σ 2 ) N(\theta,\sigma^2) N(θ,σ2) 先验分布为正态分布 N ( μ , τ 2 ) N(\mu,\tau^2) N(μ,τ2) 样本 x i ( i 1 , 2 , ⋯ , n ) x_i(i1,2,\cdots,n) xi(i1…...
浅聊MQ之Kafka与RabbitMQ简用
(前记:内容有点多,先看目录再挑着看。) Kafka与RabbitMQ的使用举例 Kafka的使用举例 安装与启动: 从Apache Kafka官网下载Kafka中间件的运行脚本。解压后,通过命令行启动Zookeeper(Kafka的运行…...
服务器被暴力破解的一次小记录
1. 网络架构 家里三台主机,其他一台macmini 启用ollama运行大模型的服务,主机1用来部署一些常用的服务如:mysql, photoprism等,服务器作为网关部署docker, 并且和腾讯云做了内网穿透。服务器部署了1panel用来管理服务并且监控&…...
3. 导入官方dashboard
官方dashboard:https://grafana.com/grafana/dashboards 1. 点击仪表板 - 新建 - 导入 注:有网络的情况想可以使用ID,无网络情况下使用仪表板josn文件 2. 在官方dashboard网页上选择符合你现在数据源的dashboard - 点击进入 3. 下拉网页选…...
国家队出手!DeepSeek上线国家超算互联网平台!
目前,国家超算互联网平台已推出 DeepSeek – R1 模型的 1.5B、7B、8B、14B 版本,后续还会在近期更新 32B、70B 等版本。 DeepSeek太火爆了!在这个春节档,直接成了全民热议的话题。 DeepSeek也毫无悬念地干到了全球增速最快的AI应用。这几天,国内的云计算厂家都在支持Dee…...
第6章 6.4 ASP.NET Core Web API各种技术及选择
6.4.1 控制器父类用哪个 6.2小节和6.3小节所演示的ASP.NET Core Web API 的控制器类都继承自ControllerBase,而6.1中MVC的控制器继承自Controller,Controller又继承自ControllerBase。 所以,一般情况下,编写的WebAPI控制器类继承…...
python导入模块的方式
在python开发中,巧用模块导入可简化开发,提高开发效率。下面简介下模块使用使用事项: 一、模块的使用: 模块 就好⽐是 ⼯具包,要想使⽤这个⼯具包中的⼯具,就需要 使用import导⼊ 这个模块每⼀个以扩展名…...
【Linux】Ubuntu Linux 系统——Node.js 开发环境
ℹ️大家好,我是练小杰,今天星期五了,同时也是2025年的情人节,今晚又是一个人的举个爪子!! 🙂 本文是有关Linux 操作系统中 Node.js 开发环境基础知识,后续我将添加更多相关知识噢&a…...
使用pyCharm创建Django项目
使用pyCharm创建Django项目 1. 创建Django项目虚拟环境(最新版版本的Django) 使用pyCharm的创建项目功能,选择Django,直接创建。 2. 创建Django项目虚拟环境(安装特定版本) 2.1创建一个基础的python项目 2.2 安装指定版本的D…...
【前端框架】深入Vue 3组件开发:构建高效灵活的前端应用
一、引言 Vue 3作为一款流行的前端框架,其组件化系统是构建大型应用的核心。通过将应用拆分为多个可复用的组件,不仅能提高代码的可维护性与复用性,还能让开发团队进行高效的协作。本文将深入探讨Vue 3组件开发的各个方面,帮助开…...
基于Python flask-sqlalchemy的SQLServer数据库管理平台
适应场景: 主要用于帮助DBA自动化很多日常工作,包括: 数据库状态监控 性能问题诊断 日志分析 自动巡检 问题告警 系统截图: main.py from flask import Blueprint, render_template, request, flash, redirect, url_for f…...
npm运行Vue项目报错 error:0308010c:digital envelope routines::unsupported
大家好,我是 程序员码递夫。 问题 VSCode 运行Vue项目,提示错误: building 2/2 modules 0 activeError: error:0308010c:digital envelope routines::unsupported 解决方法 原因是 npm 高版本(大于17),对ssl的处理做了改进&…...
计数排序
目录 计数排序原理和步骤: 完整代码实现: 计数排序原理和步骤: 当一段数据比较集中在一个范围,比如 98,95,98,91,90,93,94,97,93&…...
MyBatis拦截器终极指南:从原理到企业级实战
在本篇文章中,我们将深入了解如何编写一个 MyBatis 拦截器,并通过一个示例来展示如何在执行数据库操作(如插入或更新)时,自动填充某些字段(例如 createdBy 和 updatedBy)信息。本文将详细讲解拦…...
Pythong 解决Pycharm 运行太慢
Pythong 解决Pycharm 运行太慢 官方给Pycharm自身占用的最大内存设低估了限制,我的Pycharm刚开始默认是256mb。 首先找到自己的Pycharm安装目录 根据合适自己的改 保存,重启Pycharm...
双ESP8266-01S通讯UDP配置
第一台ESP8266(发送命令需要勾---发送新行) ATCWMODE3 ATCWSAP_DEF"CAR_wifi_Master","12345678",5,3 //设置本地wifi名称以及密码 ATCIPSTA_DEF"192.168.4.1" //设置本地IP ATCIFSR …...
Molecular Communication(分子通信)与 Molecular Semantic Communication(分子语义通信)
1. 引言 随着传统无线通信在极端环境(如微观生物体内、海洋深处)中的局限性凸显,分子通信(Molecular Communication, MC)成为一种新型通信范式。分子通信通过分子作为信息载体,在纳米尺度上传输信息&#…...
Cookie:网页浏览背后的“小秘密”
在现代互联网的世界里,Cookie 是一个几乎无处不在的概念。它不仅影响着我们的网页浏览体验,还在背后默默地支持着许多网站的功能和服务。本文将带你全面了解 Cookie 的原理、作用、安全性以及如何管理它们。 一、什么是 Cookie? Cookie 是一…...
日语学习-日语知识点小记-构建基础-JLPT-N4N5阶段(6):動詞ない形について句型
日语学习-日语知识点小记-构建基础-JLPT-N4&N5阶段(6):動詞ない形について句型 1、前言(1)情况说明(2)工程师的信仰2、知识点(1)~動詞な形 +なければなりません(2)~動詞な形 + なくてもいいです(3)に まで までに :区別3、单词(1)日语单词…...
华纳云:如何从服务器日志中发现僵尸进程?
在 CentOS 系统中,僵尸进程通常指那些已经完成执行但仍然在进程表中存在的进程。它们没有实际的执行,但仍然占用系统资源,通常会出现在父进程没有及时回收子进程的状态下。虽然僵尸进程本身不消耗 CPU 或内存资源,但它们会占用进程…...
fastadmin 接口请求提示跨域
问题描述 小程序项目,内嵌h5页面,在h5页面调用后端php接口,提示跨域。网上查找解决方案如下: 1,设置header // 在入口文件index.php直接写入直接写入 header("Access-Control-Allow-Origin:*"); header(&q…...
