【C++进阶学习】第八弹——红黑树的原理与实现——探讨树形结构存储的最优解

二叉搜索树:【C++进阶学习】第五弹——二叉搜索树——二叉树进阶及set和map的铺垫-CSDN博客

AVL树:

​​​​​​【C++进阶学习】第七弹——AVL树——树形结构存储数据的经典模块-CSDN博客

前言:

在前面,我们已经学习了二叉搜索树和它的改进AVL树,今天,我们来学习另一种由二叉搜索树改进而来的树形结构——红黑树

目录

一、红黑树的概念

二、红黑树的性质

三、红黑树的节点结构

四、红黑树的操作

五、红黑树的实现代码

六、总结


一、红黑树的概念

红黑树是一种特殊的二叉树,它的每个节点都增加一个存储为表示颜色,要么是黑色,要么是白色。并且通过对每条路径上添加节点时节点的颜色限制,来确保每个路径上的黑色节点数量一致,且最长路径长度最多是最短路径长度的两倍,因此达到平衡。

二、红黑树的性质

红黑树有以下五个性质:

  1. 节点是红色或黑色:每个节点都有一个颜色属性,颜色可以是红色或黑色。

  2. 根节点是黑色:树的根节点必须是黑色。

  3. 红色节点的子节点是黑色:如果一个节点是红色,则它的两个子节点必须是黑色(即不能有两个连续的红色节点)。

  4. 每个节点到其每个叶子节点的路径都包含相同数量的黑色节点:从任何节点到其每个叶子节点的路径上,经过的黑色节点的数量必须相同。

  5. 叶子节点是黑色:红黑树的叶子节点(通常是指空节点)被视为黑色。

三、红黑树的节点结构

template<class K, class V>
struct BSTreeNode
{BSTreeNode<K, V>* _left;    //左子树BSTreeNode<K, V>* _right;   //右子树BSTreeNode<K, V>* _parent;  //父亲pair<K, V> _kv;       //存放节点值的string _col;    //颜色(通过这个可以直到左右子树存在情况)//构造函数BSTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col("RED")     //默认颜色为红色{}
};

红黑树的节点结构与二叉搜索树和AVL树差别不大,最大的差别就是加入了一个新的存储点——颜色

四、红黑树的操作

红黑树的基本操作包括插入、删除和查找。以下是这些操作的简要说明:

  1. 插入

    • 将新节点插入到树中,初始时将其标记为红色。
    • 可能会破坏红黑树的性质,需要通过旋转和重新着色来恢复性质。
  2. 删除

    • 删除节点后,可能会破坏红黑树的性质。
    • 需要进行调整,包括旋转和重新着色,以恢复红黑树的性质。
  3. 查找

    • 查找操作与普通的二叉搜索树相同,通过比较节点值来决定向左或向右子树查找。

在这几个操作中,插入是红黑树的关键,因为这是构造红黑树的关键,怎样插入才能保证红黑树的节点颜色、路径长度符合规定,下面我们就来重点讲解一下红黑树的节点插入操作:

首先我们要先清楚一点,红黑树也是二叉搜索树,所以它肯定是符合二叉搜索树的性质的——左边子树值小于根,右边子树值大于根

问题的关键是如何保证插入后结构的颜色符合规定,要做到这一点,我们首先就先要摸清插入节点会遇到的所有情况,然后我们再分析如何解决这些情况:

(强调一下:一定要把前面AVL树中讲的左右旋先弄明白)

假设:cur表示当前节点,p表示父节点,g表示祖父节点,u为叔叔节点

情况一:cur为红,p为黑

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

解决方法:把p、u变成黑,g变为红,然后让g变为cur继续向上处理 

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

解决方法:把p变为黑,g变为红,然后进行左旋/右旋

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

解决方法:把p变为黑,g变为红,同时右旋/左旋

特殊情况:如果当cur的插入位置形似AVL树中的RL型或LR型时,要先进行旋转转换成上面几种情况

五、红黑树的实现代码

RBTree.h

//红黑树
#include<iostream>
using namespace std;template<class K, class V>
struct BSTreeNode
{BSTreeNode<K, V>* _left;    //左子树BSTreeNode<K, V>* _right;   //右子树BSTreeNode<K, V>* _parent;  //父亲pair<K, V> _kv;       //存放节点值的string _col;    //颜色(通过这个可以直到左右子树存在情况)//构造函数BSTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col("RED")     //默认颜色为红色{}
};template<class K, class V>
class BSTree
{typedef BSTreeNode<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)       //p为g的左孩子时{//     g//   p       Node* uncle = grandfather->_right;if (uncle && uncle->_col == "RED")    //u存在且为红{//       g//    p      u// curparent->_col = uncle->_col = "BLACK";grandfather->_col = "RED";//继续往上更新cur = grandfather;parent = cur->_parent;}else                               //u不存在/u存在且为黑{//       g//     p//  curif (cur == parent->_left){RotateR(grandfather);grandfather->_col = "RED";parent->_col = "BLACK";}else{//LR型RotateL(parent);RotateR(grandfather);cur->_col = "BLACK";grandfather->_col = "RED";}break;}}else                      //p为g的右孩子时,与上面的相反即可{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){//       g//     u    p//            cRotateL(grandfather);grandfather->_col = "RED";parent->_col = "BLACK";}else{//       g//     u   p//        c//RL型RotateR(parent);RotateL(grandfather);cur->_col = "BLACK";grandfather->_col = "RED";}break;}}}_root->_col = "Black";return true;}//进行旋转调整(旋转操作与AVL树中的完全一样,不明白的可以看我之前的文章)void RotateL(Node* parent)   //左单旋{Node* subR = parent->_right;Node* subRL = subR->_left;Node* parentParent = parent->_parent;parent->_right = subRL;subR->_left = parent;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;}parent->_parent = subR;}void RotateR(Node* parent)   //右单旋{Node* subL = parent->_left;Node* subLR = subL->_right;Node* parentParent = parent->_parent;parent->_left = subLR;subL->_right = parent;if (subLR){subLR->_parent = parent;}if (_root == parent){_root = subL;subL->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}parent->_parent = subL;}//中序打印void Inorder(){_Inorder(_root);cout << endl;}void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_kv.first << " ";_Inorder(root->_right);}//检查是否为BS树(检查两点)//一:是否有连续的红节点//二:各个路径上的黑色节点个数是否相等bool Check(Node* root,int blacknum,const int refVal){if (root == nullptr){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;
};

RBTree.c

//红黑树
int main()
{int a[] = { 4,2,6,1,3,5,15,7,16,14 };   //int a[] = { 790,760,969,270,31,424,377,24,702 };BSTree<int, int> t;for (auto e : a){t.Insert(make_pair(e, e));}t.Inorder();cout << "是否是红黑树(1/0):"<<t.IsBalance() << endl;return 0;
}

运行结果:

六、总结

以上就是红黑树的全部内容,红黑树因为其自平衡的特性,及通过节点颜色来操作其树形结构的特点,极大的提高了数据存储及处理的效率,需要我们好好掌握

感谢各位大佬观看,创作不易,还请各位大佬一键三连!!!

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

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

相关文章

PCIe 6.0为什么需要14-bit tag

1.TLP中的tag是什么 在PCIe TLP&#xff08;Transaction Layer Packet&#xff09;中&#xff0c;tag是分配给特定Non-Posted Request的编号&#xff0c;协议要求CPL/CPLD中的tag 与对应non-post request TLP中的tag保持一致&#xff0c;因此Requester可以使用tag来识别CPL…

Windows通过命令查看mac : getmac

要查看本机网卡mac&#xff0c;可以通过ipconfig /all 显示&#xff0c;但输出内容过多 可以通过getmac命令查看 示例 C:\Users\Desktop> getmac物理地址 传输名称暂缺 没有硬件 1C-1B-B5-04-E2-7D \Device\Tcpip_{80096E40-D51D-490C-9AF7-…

Java 扫雷游戏

程序分析 使用Java编写的扫雷游戏界面程序&#xff0c;主要内容总结如下&#xff1a; Frame类继承自JFrame&#xff0c;构建了扫雷游戏的界面。 包含文本框text、标签nowBomb和setBomb、按钮start、面板MenuPamel和bombPanel等组件。通过jbInit方法进行初始化设置&#xff0c;…

note24:表分区规范

目录 分区设计原则 分区维护 存储方式及分布键规范 分区设计原则 表分区用于解决数据量特别大的表的问题&#xff0c;比如事实表&#xff0c;解决办法就是将表分成很多小且更容易管理的部分。 表分区参考以下几个原则 &#xff08;1&#xff09;表是否足够大&#xff1f;…

免费【2024】springboot 趵突泉景区的智慧导游小程序

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

MATLAB基础:数组

今天我们继续学习MATLAB中的数组 我们在学习MATLAB时了解到&#xff0c;MATLAB作者秉持着“万物皆可矩阵”的思想企图将数学甚至世间万物使用矩阵表示出来&#xff0c;而矩阵的处理&#xff0c;自然成了这门语言的重中之重。 数组基础 在MATLAB中&#xff0c;数组是一个基本…

十、SpringBoot 统⼀功能处理【拦截器、统一数据返回格式、统一异常处理】

十、SpringBoot 统⼀功能处理 1. 拦截器【HandlerInterceptor、WebMvcConfig】1.1 拦截器快速⼊⻔⾃定义拦截器&#xff1a;实现HandlerInterceptor接⼝&#xff0c;并重写其所有⽅法注册配置拦截器&#xff1a;实现WebMvcConfigurer接⼝&#xff0c;并重写addInterceptors⽅法…

堆(c++)

堆是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。 堆总是满足下列性质&#xff1a; 堆中某个节点的值总是不大于或不小于其父节点的值&#xff1b;堆总是一棵完全二叉树。 常见的堆有二叉堆、斐波那契堆等。 堆是非线性数据结构&#…

初识C++ · map和set的使用

目录 前言&#xff1a; 1 set 2 map 前言&#xff1a; 在前面阶段&#xff0c;我们已经学习了stl里面的部分容器&#xff0c;比如vector,list,deque等&#xff0c;这些容器都被称为序列式容器&#xff0c;也就是每个值之间式没有关联的&#xff0c;那么今天介绍的容器&…

【笔记本触摸屏】超级好用技巧

选中文字&#xff1a;点一下要复制的文字开头&#xff0c;按住shift键不放&#xff0c;然后点一下你想要的文字结尾滚动&#xff1a;双指向 水平 或者 垂直 方向滑动放大或者缩小: 将两个手指放在触摸板上&#xff0c;让后收缩后者拉伸显示更多命令&#xff08;类似于右键单击&…

四十九、 通过境内数据交易所进行跨境数据贸易应考虑哪些跨境数据合规问题?

根据中国信通院数据显示&#xff0c;2023 年我国数字经济规模可达 56.1 万亿元, 数字经济占 GDP 比重接近于第二产业&#xff0c;占国民经济的比重&#xff0c;达到 40%以上。伴随数字技术兴起以及各项数字经济相关的政策和法律的落地&#xff0c;以跨境数据流动为底层支撑的跨…

智能音箱和普通音箱有什么区别

智能音箱和普通音箱在多个方面存在显著的区别&#xff0c;主要包括设计目的、功能特点、连接方式、音质表现以及交互方式等。 一、设计目的和功能特点 智能音箱&#xff1a;设计目的不仅仅是为了播放音乐&#xff0c;更重要的是集成了语音识别和语音交互功能&#xff0c;成为…

IGV.js | 载入自己下载的gtf文件

1.安装 htslib-1.20 https://www.htslib.org/doc/tabix.html J3$ cd ~/Downloads/ $ wget https://github.com/samtools/htslib/releases/download/1.20/htslib-1.20.tar.bz2 $ tar jxvf htslib-1.20.tar.bz2编译安装&#xff1a; $ cd htslib-1.20/ $ ./configure --prefix/…

ts -> class -> abstract

在TypeScript中&#xff0c;你可以直接使用abstract关键字来定义抽象类和抽象方法。抽象类不能被实例化&#xff0c;而抽象方法必须在派生类中被实现。以下是一个具体的例子&#xff1a; abstract class Animal {name: string;constructor(name: string) {this.name name;}//…

C#中的Action

C#中的Action是一种委托类型&#xff0c;‌用于引用不返回值的方法。‌Action可以接受0到16个参数&#xff0c;‌并且不返回任何值。‌它是一种通用的委托类型&#xff0c;‌非常方便用于处理不同参数和不同函数签名的情况。‌Action的用法包括声明Action委托类型、‌创建Actio…

vue的三大核心知识点

响应式&#xff1a; 监听data属性getter setter(包括数组)模板编译&#xff1a; 模板到render函数再到vnodevdom&#xff1a; patch(elem, vnode)和patch(vnode, newVnode) vue组件初次渲染过程 解析模板为render函数&#xff08;或在开发环境已完成&#xff0c;vue-loader&a…

WIX Toolset 3.11 对本地化的支持方案

1.准备主题文件和本地化文件 WIX Toolset种主题文件为xml文件&#xff0c;负责配置控件的布局&#xff0c; 本地化文件为wxl文件&#xff0c;负责配置待加载的字符串&#xff0c;主题文件根据ID加载需要显示的文字内容。考虑到英文和中文字符长度大小不一&#xff0c;所以这里…

渗透测试——prime1靶场实战演练{常用工具}端口转发

文章目录 概要信息搜集 概要 靶机地址&#xff1a;https://www.vulnhub.com/entry/prime-1,358 信息搜集 nmap 扫网段存活ip及端口 找到除了网关外的ip&#xff0c;开放了80端口&#xff0c;登上去看看 是一个网站&#xff0c;直接上科技扫一扫目录 python dirsearch.py -u …

尝试带你理解 - 进程地址空间,写时拷贝

序言 在上一篇文章 进程概念以及进程状态&#xff0c;我们提到了 fork 函数&#xff0c;该函数可以帮我们创建一个子进程。在使用 fork 函数时&#xff0c;我们会发现一些奇怪的现象&#xff0c;举个栗子&#xff1a; 1 #include <stdio.h>2 #include <unistd.h>3 …

跟《经济学人》学英文:2024年07月20日这期 The Russell 2000 puts in a historic performance

Why investors have fallen in love with small American firms The Russell 2000 puts in a historic performance 罗素2000指数&#xff1a; 罗素2000指数&#xff08;英语&#xff1a;Russell 2000 Index&#xff09;为罗素3000指数中收录市值最小的2000家&#xff08;排序…