RB-tree(红黑树)详解

RB-tree(红黑树)

红黑树的规则如下:

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

2.根节点为黑色

3.如果节点为红色,那么它的子节点必须为黑色

4.任何一个节点到NULL(树的尾端)的任何路径所包含的黑节点个数相同

简而言之就是每个路径的黑色节点数量相同

新增的节点必须为红,因为增加红色节点所要付出的代价会比黑色的要小很多,如果新增节点为黑色,那么就会打破第四条规则,整棵树都要调整,代价是相当之大的

红黑树节点的定义

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():_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv){}
};

红黑树的插入操作

红黑树的插入操作分为几种情况

情况一:

当cur为红、parent为红、grandparent为黑、uncle存在并且为红

操作过程:

将parent和uncle全部染成黑色,grandparent染成红色,然后把cur给grandparent继续向上判断,如果cur的parent依旧为红色,那么就要继续染色,如果cur为_root了,将根节点染成黑色。

具象图:

抽象图:

在抽象图当中,在a或者b子树当中触发了染色机制,然后一路染色上来,一直到cur也染成了红色,cur和p就造成冲突,需要继续染色。

情况二:

当cur为红,p为红,g黑,u不存在/u为黑

操作:

p为g的左孩子,cur为p的左孩子,则进行右单旋转;

相反,p为g的右孩子,cur为p的右孩子,则进行左单旋转

最后p变为黑色,cur和g变为红色

具象图:

当uncle为空的时候

抽象图:

情况三:

当cur和p为红,g为黑,u为黑/不存在

如果parent为grandparent的左儿子,对parent左旋,再对grandparent右旋

如果parent为grandparent的右儿子,对parent右旋,再对grandparent左旋

cur染为黑色,cur和grandparent染为红色。

具象图:

抽象图:

红黑树+测试代码

#pragma once
#include"AVLTree.h"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){}
};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;}else{parent->_left = cur;}cur->_parent = parent;//检查是否满足红黑树的要求while (parent && parent->_col == Red){//如果父亲节点的颜色为红色,那么就有可能需要改变颜色或者旋转Node* grandparent = parent->_parent;assert(grandparent);assert(grandparent->_col == Black);//父亲为红色,那么祖父一定为黑色,不然之前的插入就存在问题if (parent == grandparent->_left){Node* uncle = grandparent->_right;//情况一,uncle存在并且uncle为红色,就要继续往上染色if (uncle && uncle->_col == Red){parent->_col = uncle->_col = Black;grandparent->_col = Red;cur = grandparent;parent = cur->_parent;}else{//uncle不存在或者uncle为黑色就会走到这里//情况二+情况三if (cur == parent->_left){// 情况二:右单旋+变色//     g //   p   u// c//右旋+染色RotateR(grandparent);parent->_col = Black;grandparent->_col = Red;}else{// 情况三:左右单旋+变色//     g //   p   u//     cRotateL(parent);RotateR(grandparent);cur->_col = Black;parent->_col = grandparent->_col = Red;}break;}}else{Node* uncle = grandparent->_left;if (uncle && uncle->_col == Red){parent->_col = uncle->_col = Black;grandparent->_col = Red;cur = grandparent;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandparent);parent->_col = Black;grandparent->_col = Red;}else{RotateR(parent);RotateL(grandparent);cur->_col = Black;grandparent->_col = Red;}break;}}}_root->_col = Black;return true;}void InOrder(){_InOrder(_root);cout << endl;}bool IsBalance(){if (_root == nullptr){return true;}if (_root->_col == Red){cout << "根节点不是黑色" << endl;return false;}// 黑色节点数量基准值int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == Black)++benchmark;cur = cur->_left;}return PrevCheck(_root, 0, benchmark);}private:bool PrevCheck(Node* root, int blackNum, int& benchmark){if (root == nullptr){if (blackNum != benchmark){cout << "某条黑色节点的数量不相等" << endl;return false;}else{return true;}}if (root->_col == Black){++blackNum;}if (root->_col == Red && root->_parent->_col == Red){cout << "存在连续的红色节点" << endl;return false;}return PrevCheck(root->_left, blackNum, benchmark)&& PrevCheck(root->_right, blackNum, benchmark);}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;Node* pparent = parent->_parent;parent->_right = subRL;if (subRL){subRL->_parent = parent;}subR->_left = parent;parent->_parent = subR;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (pparent->_left == parent){pparent->_left = subR;}else{pparent->_right = subR;;}subR->_parent = pparent;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;Node* pparent = parent->_parent;//防止空指针if (subLR){subLR->_parent = parent;}subL->_right = parent;parent->_parent = subL;if (parent == _root){//如果parent就是根节点_root = subL;subL->_parent = nullptr;}else{//如果parent只是一颗子树的根节点,就还需要连接好parent//判断是左还是右节点if (pparent->_left == parent){pparent->_left = subL;}else{pparent->_right = subL;}subL->_parent = pparent;}}Node* _root = nullptr;
};void TestRBTree2()
{size_t N = 1000;srand(time(0));RBTree<int, int> t1;for (size_t i = 0; i < N; ++i){int x = rand();cout << "Insert:" << x << ":" << i << endl;t1.Insert(make_pair(x, i));}cout << "IsBalance:" << t1.IsBalance() << endl;
}

运行结果:

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

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

相关文章

模拟量输出FC S_RTI(信捷C语言源代码)

模拟量输出FC SCL源代码请查看下面博客: PLC模拟量输出 模拟量转换FC S_RTI_博途模拟量转换指令_RXXW_Dor的博客-CSDN博客1、本文主要展示西门子博途模拟量输出转换的几种方法, 方法1:先展示下自编FC:计算公式如下:intput intput Real ISH Real //工程量上限 ISL Real //工…

Linux软件包管理工具-dpkg

目录 前言 一、什么是.deb 二、安装软件包 三、卸载软件包 四、查询软件包状态 五、查询已安装的软件包列表 六、解决依赖问题 前言 dpkg是Debian和Ubuntu等Linux发行版中用于管理软件包的基本包管理工具 dpkg的一些常用命令&#xff0c;通过这些命令&#xff0c;你可…

【数据挖掘】将NLP技术引入到股市分析

一、说明 在交易中实施的机器学习模型通常根据历史股票价格和其他定量数据进行训练&#xff0c;以预测未来的股票价格。但是&#xff0c;自然语言处理&#xff08;NLP&#xff09;使我们能够分析财务文档&#xff0c;例如10-k表格&#xff0c;以预测股票走势。 二、对自然语言处…

智能柜台配置项配置指南

1.全局配置config.xml 配置项说明 <configuration><param key="env" value="production" display=""/> </configuration>root节点:configuration 配置节点:param 配置项名:param 的 key属性配置项值:param 的 value属性配…

【OpenCV】常见问题及解决办法

文章目录 0 前言1 中文乱码问题2 非法路径问题 0 前言 本篇博客主要是总结OpenCV使用过程中遇到的一些问题&#xff0c;以及对应的解决办法&#xff0c;这里重点是关注OpenCV&#xff0c;既有基于C的&#xff0c;也有基于Python的&#xff0c;比较全面&#xff0c;而且也会随着…

【Linux后端服务器开发】TCP通信设计

目录 一、TCP通信协议的封装 二、TCP多进程通信 三、TCP多线程通信 一、TCP通信协议的封装 简单的TCP一对一通信其实完全可以不进行封装&#xff0c;直接分别写server端和client端的源代码&#xff0c;按照TCP通信协议的规定调用socket接口即可完成&#xff0c;但是在通过T…

RocketMQ教程-安装和配置

Linux系统安装配置 64位操作系统&#xff0c;推荐 Linux/Unix/macOS 64位 JDK 1.8 Maven3.0 yum 安装jdk8 yum 安装maven 1.下载安装Apache RocketMQ RocketMQ 的安装包分为两种&#xff0c;二进制包和源码包。 点击这里 下载 Apache RocketMQ 5.1.3的源码包。你也可以从这…

Windows11的VS201x编译OpenCV+Contrib+CUDA

(1) CUDA下载&#xff0c;注意要和cudnn版本号相关。 我安装的是cuda11.0,注意VS2015不能编译CUDA11&#xff0c;所以用VS2015的话需要下载CUDA 10。因为更高的版本目前还没有cudnn。 (2) 下载和安装VS2015。 (3) 下载和解压CMake。 CMake地址&#xff1a; Releases Kitw…

Nginx代理nginx.conf配置——nginx对静态文件代理

1. 对根目录下的静态资源代理 Nginx代理nginx.conf配置——反向代理 2. 目录代理 如果需要将资源代理到不同的目录下&#xff0c;则在nginx.conf中的server节点下进行如下配置&#xff1a; location /image {root /opt/cache; }location vedio {root /opt/cache; } 修改后…

Android dp to pix resources.getDimension(R.dimen.xxx) ,kotlin

Android dp to pix resources.getDimension(R.dimen.xxx) ,kotlin <?xml version"1.0" encoding"utf-8"?> <resources><dimen name"my_size_dp">20dp</dimen><dimen name"my_size_px">20px</dime…

【微信小程序】从网络请求返回值res.data获取并解析一个Array

在微信小程序中&#xff0c;可以通过res.data获取到请求返回的数据。如果返回的数据是一个数组&#xff0c;您可以直接对其进行操作。 以下是一个示例代码&#xff0c;演示了如何从res.data中解析一个数组&#xff1a; wx.request({url: http://yuor-api:80/device/_query/no…

双线性插值算法缩放图片,部分图片出现黑边的解决办法

因工作需要使用软件方法缩放PNG图片&#xff0c;询问chatgpt拿到了c双线性插值算法&#xff0c;开始很顺利&#xff0c;整理一下代码&#xff0c;封装一下接口&#xff0c;就可以使用了&#xff0c;效果还不错&#xff0c;马上编译发给测试组测试&#xff0c;测试发现有一些图片…

数据仓库表设计理论

数据仓库表设计理论 数仓顾名思义是数据仓库&#xff0c;其数据来源大多来自于业务数据(例如:关系型数据库)&#xff0c;当设计数仓中表类型时(拉链表、增量表、全量表、流水表、切片表)时&#xff0c;应先观察业务数据的特点再设计数仓表结构 首先业务数据是会不断增长的-即…

练习——动态内存分配的笔试题

今天我们分享几道经典的笔试题&#xff0c;做完直接变成陈泽 第一题 ~~ --------------------------------------------------------------------------------------------------~~ void GetMemory(char* p) {p (char*)malloc(100); } void Test(void) {char* str NULL;Get…

阿里云容器镜像仓库(ACR)的创建和使用

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

使用spark进行hbase的bulkload

使用spark进行hbase的bulkload 一、 背景 HBase 是一个面向列&#xff0c;schemaless&#xff0c;高吞吐&#xff0c;高可靠可水平扩展的 NoSQL 数据库&#xff0c;用户可以通过 HBase client 提供的 put get 等 api 实现在数据的实时读写。在过去的几年里&#xff0c;HBase …

虚拟机 NAT模式联网 重启断网的解决方法

1、虚拟机NAT模式联网 这个博客写的很清楚:按照他的方法可以ping通www.baidu.com 三分钟完成虚拟机联网 小白看了都说好&#xff01;&#xff01;&#xff01; 虚拟机超详细联网教程/步骤 SDN软件定义网络实验_虚拟机联网教程_九号迷妹的博客-CSDN博客 但是虚拟机重启几次之后…

使用arm-none-eabi-gcc编译器搭建STM32的Vscode开发环境

工具 make&#xff1a;Windows中没有make&#xff0c;但是可以通过安装MinGW或者MinGW-w64&#xff0c;得到make。gcc-arm-none-eabi&#xff1a;建议最新版&#xff0c;防止调试报错OpenOCDvscodecubeMX VSCODE 插件 Arm Assembly&#xff1a;汇编文件解析C/C&#xff1a;c…

HTTP 请求走私漏洞(HTTP Request Smuggling)

一、什么是Http 请求走私漏洞&#xff1f; HTTP请求走私漏洞&#xff08;HTTP Request Smuggling&#xff09;是一种安全漏洞&#xff0c;利用了HTTP协议中请求和响应的解析和处理方式的不一致性。攻击者通过构造特定的恶意请求&#xff0c;以欺骗服务器和代理服务器&#xff0…

Godot 4 源码分析 - 增加管道通信

学习研究Godot 4&#xff0c;很爽&#xff0c;虽然很庞杂&#xff0c;但相对于自己的水平来说&#xff0c;很强大&#xff0c;尤其是vulkan这块直接打包可用&#xff0c;省得自己从头琢磨。 一点一点地消化、优化与完善&#xff0c;最终才能成为自己的。 这段时间就在Godot的…