【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗)



快乐的流畅:个人主页


个人专栏:《C语言》《数据结构世界》《进击的C++》

远方有一堆篝火,在为久候之人燃烧!

文章目录

  • 引言
  • 一、红黑树的概念
  • 二、红黑树的模拟实现
    • 2.1 结点
    • 2.2 成员变量
    • 2.3 插入
      • 情况一:uncle在左,parent在右
        • ==如果uncle存在且为红色==:
        • ==如果uncle不存在,或者存在且为黑色==:
      • 情况二:parent在左,uncle在右
        • ==如果uncle存在且为红色==:
        • ==如果uncle不存在,或者存在且为黑色==:
  • 三、红黑树的验证
  • 四、红黑树的性能
    • 4.1 优势
    • 4.2 适用场景

引言

之前学习的AVL树,是一种平衡二叉搜索树,它追求绝对平衡,从而导致插入和删除性能较差。而今天学习的红黑树,是另一种平衡二叉搜索树,它追求相对平衡,使得增删查改的性能都极佳,时间复杂度皆为O(log2N),是数据结构中的精华,天才般的设想!

一、红黑树的概念

红黑树,顾名思义,其节点有红和黑两种颜色。

之所以新增结点颜色的标记,是因为通过结点着色方式的限制,能够让红黑树的最长路径不超过最短路径的两倍,以保证相对平衡。


红黑树满足五条性质:

  1. 所有结点非黑即红
  2. 根结点为黑色
  3. NIL结点为黑色
  4. 红色结点的子结点必为黑色
  5. 任意结点到其叶子NIL结点的所有路径,都包含相同的黑色结点

在红黑树中,NIL节点(也称为空节点)是叶子节点的一种特殊表示。它们不是实际存储数据的节点,而是树结构中的占位符,用于定义树的边界。所有的红黑树都以NIL节点为叶子节点,这些NIL节点在视觉上通常不被画出来。


性质解读:

  • 性质4:表明不能有连续的红色结点
  • 性质4+性质5:
    • 理论最短路径:全为黑色结点
    • 理论最长路径:红黑相间

这样,就保证了最长路径不超过最短路径的两倍。

二、红黑树的模拟实现

2.1 结点

enum Color
{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;Color _col;RBTreeNode(const pair<K, V>& kv): _left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED){}
};

细节:

  1. 使用三叉链,增加了指向parent的指针
  2. 使用KV模型,数据存储键值对pair
  3. 结点储存颜色,同时颜色使用枚举
  4. 结点的颜色初始化为红色

说明:为什么结点的颜色初始化为红色呢?因为插入新节点时(不为根部),如果插入黑色,就会直接破坏性质5,导致每条路径黑结点数目不同;而如果插入红色,有可能不会破坏性质4,所以结点初始化为红色更优。

2.2 成员变量

template<class K, class V>
class RBTree
{
protected:typedef RBTreeNode<K, V> Node;
public:
protected:Node* _root = nullptr;
};

2.3 插入

因为红黑树也是二叉搜索树,所以默认成员函数和遍历与之前写的没什么不同,这里重点讲解红黑树的插入。

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);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;if (grandparent->_right == parent)//uncle在左,parent在右{Node* uncle = grandparent->_left;if (uncle && uncle->_col == RED)//uncle为红,变色+向上调整{parent->_col = uncle->_col = BLACK;grandparent->_col = RED;cur = grandparent;parent = cur->_parent;}else//uncle为空或为黑,变色+旋转{if (parent->_right == cur)//左单旋{RotateL(grandparent);parent->_col = BLACK;grandparent->_col = RED;}else//右左旋{RotateR(parent);RotateL(grandparent);cur->_col = BLACK;grandparent->_col = RED;}}}else//parent在左,uncle在右{Node* uncle = grandparent->_right;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandparent->_col = RED;cur = grandparent;parent = cur->_parent;}else{if (parent->_left == cur)//右单旋{RotateR(grandparent);parent->_col = BLACK;grandparent->_col = RED;}else//左右旋{RotateL(parent);RotateR(grandparent);cur->_col = BLACK;grandparent->_col = RED;}}}}_root->_col = BLACK;return true;
}

思路:

  1. 以二叉搜索树的方式正常插入
  2. 讨论并调整结点的颜色,以及调整结构,使之满足红黑树的性质

循环条件:while (parent && parent->_col == RED)

保证了parent存在且为红,grandparent存在且为黑


情况一:uncle在左,parent在右

如果uncle存在且为红色

处理方法:

  1. 将parent和uncle变黑,grandparent变红
  2. cur = grandparent,parent = cur->_parent,继续向上调整
  3. 防止grandparent为根节点却变红,在循环结束后将根节点变为黑色
如果uncle不存在,或者存在且为黑色

当cur在右部外侧时:

处理方法:

  1. 先对grandparent进行左单旋
  2. 再将parent变黑,grandparent变红

当cur在右部内侧时:

处理方法:

  1. 先对parent进行右单旋
  2. 再对grandparent进行左单旋
  3. 最后将cur变黑,grandparent变红

情况二:parent在左,uncle在右

如果uncle存在且为红色

处理方法:

  1. 将parent和uncle变黑,grandparent变红
  2. cur = grandparent,parent = cur->_parent,继续向上调整
  3. 防止grandparent为根节点却变红,在循环结束后将根节点变为黑色
如果uncle不存在,或者存在且为黑色

当cur在左部外侧时:

处理方法:

  1. 先对grandparent进行右单旋
  2. 再将parent变黑,grandparent变红

当cur在左部内侧时:

处理方法:

  1. 先对parent进行左单旋
  2. 再对grandparent进行右单旋
  3. 最后将cur变黑,grandparent变红

红黑树插入的核心口诀uncle存在且为红,变色+向上调整,uncle不存在或为黑,变色+旋转


附上旋转的实现

void RotateL(Node* parent)
{Node* grandparent = parent->_parent;Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL){subRL->_parent = parent;}subR->_left = parent;parent->_parent = subR;if (grandparent){if (grandparent->_right == parent){grandparent->_right = subR;}else{grandparent->_left = subR;}}else{_root = subR;}subR->_parent = grandparent;
}void RotateR(Node* parent)
{Node* grandparent = parent->_parent;Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR){subLR->_parent = parent;}subL->_right = parent;parent->_parent = subL;if (grandparent){if (grandparent->_right == parent){grandparent->_right = subL;}else{grandparent->_left = subL;}}else{_root = subL;}subL->_parent = grandparent;
}

三、红黑树的验证

bool IsBalance()
{if (_root && _root->_col == RED){cout << "根结点为红色" << endl;return false;}int benchMark = 0;//基准值Node* cur = _root;while (cur){if (cur->_col == BLACK){++benchMark;}cur = cur->_right;}return Check(_root, 0, benchMark);
}bool Check(Node* root, int blackNum, int benchMark)
{if (root == nullptr){if (blackNum != benchMark){cout << "某条路径黑色结点数量不相等" << endl;return false;}return true;}if (root->_col == BLACK){++blackNum;}if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << "存在连续的红色结点" << endl;return false;}return Check(root->_left, blackNum, benchMark)&& Check(root->_right, blackNum, benchMark);
}

细节:

  1. 验证根节点是否为黑
  2. 先计算出一条路径的黑色结点个数作为基准值,再在递归中比较每条路径的黑色结点是否相等
  3. 若该节点为红,检测其parent是否为红,判断是否存在连续的红色节点

四、红黑树的性能

4.1 优势

红黑树是高效的平衡二叉树,增删改查的时间复杂度都是O( l o g 2 N log_2 N log2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对AVL树而言,降低了插入和旋转的次数

4.2 适用场景

因此,在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。


真诚点赞,手有余香

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

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

相关文章

工业互联网下的增强现实

工业互联网下的增强现实 1、 概述 增强现实&#xff08;Augmented Reality&#xff0c;简称AR&#xff09;&#xff0c;增强现实技术也被称为扩增现实&#xff0c;AR增强现实技术是促使真实世界信息和虚拟世界信息内容之间综合在一起的较新的技术内容&#xff0c;其将原本在现…

SQLAlchemy操作数据库

数据库是一个网站的基础。 比如 MySQL 、 MongoDB 、 SQLite 、 PostgreSQL 等&#xff0c;这里我们以 MySQL为例进行讲解。 SQLAlchemy 是一个 ORM 框架 我们会以 MySQL SQLAlchemy 组合进行讲解。 在操作数据库操作之前&#xff0c;先确保你已经安装了以下两个插件&#…

个人blog网站搭建1

写在前面 建立网站最好有一定计算机基础&#xff0c;需要了解以下几个概念&#xff1a;域名&#xff1a;网站必须需要地址&#xff0c;这样别人才能在浏览器中输入你的域名来访问你的网站&#xff0c;本质上是通过ip来访问你的网站&#xff0c;可以了解以下域名解析。服务器&a…

【每日跟读】常用英语500句(1~100)

【每日跟读】常用英语500句 What’s up? 怎么啦&#xff1f; I see. 我明白 Shut up. 闭嘴 Not bad. 还不错 I’ll do my best. 我会尽全力 Take it easy. 放轻松 On my way. 马上来 Are you serious? 你是认真的吗&#xff1f; See you later. 待会见 Good job. 做…

java Web餐馆订单管理系统用eclipse定制开发mysql数据库BS模式java编程jdbc

一、源码特点 JSP 餐馆订单管理系统是一套完善的web设计系统&#xff0c;对理解JSP java 编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,eclipse开发&#xff0c;数据库为Mysql5.0&#xff0c;使…

FPGA高端项目:解码索尼IMX390 MIPI相机转HDMI输出,提供FPGA开发板+2套工程源码+技术支持

目录 1、前言2、相关方案推荐本博主所有FPGA工程项目-->汇总目录我这里已有的 MIPI 编解码方案 3、本 MIPI CSI-RX IP 介绍4、个人 FPGA高端图像处理开发板简介5、详细设计方案设计原理框图IMX390 及其配置MIPI CSI RX图像 ISP 处理图像缓存HDMI输出工程源码架构 6、工程源码…

美团一面:说一说Java中的四种引用类型?

引言 在JDK1.2之前Java并没有提供软引用、弱引用和虚引用这些高级的引用类型。而是提供了一种基本的引用类型&#xff0c;称为Reference。并且当时Java中的对象只有两种状态&#xff1a;被引用和未被引用。当一个对象被引用时&#xff0c;它将一直存在于内存中&#xff0c;直到…

选择最佳图像处理工具OpenCV、JAI、ImageJ、Thumbnailator和Graphics2D

文章目录 1、前言2、 图像处理工具效果对比2.1 Graphics2D实现2.2 Thumbnailator实现2.3 ImageJ实现2.4 JAI&#xff08;Java Advanced Imaging&#xff09;实现2.5 OpenCV实现 3、图像处理工具结果 1、前言 SVD(stable video diffusion)开放了图生视频的API&#xff0c;但是限…

C#多态性

文章目录 C#多态性静态多态性函数重载函数重载 动态多态性运行结果 C#多态性 静态多态性 在编译时&#xff0c;函数和对象的连接机制被称为早期绑定&#xff0c;也被称为静态绑定。C# 提供了两种技术来实现静态多态性。分别为&#xff1a; 函数重载 运算符重载 运算符重载将…

基于React的低代码平台开发实践

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;在线地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

2023混合多比特层-RDHEI Based on the Mixed Multi-Bit Layer Embedding Strategy

RRBE 本文仅供自我学习记录,切勿转载和搬运,如有侵权联系立删! 方法总框架 首先,发送者将载体图像进行两轮的不重叠块分割,分为可用隐藏块(AHB)和不可用隐藏块(UHB),然后通过依次处理可用块的像素信息产生location图来创造空间,接着通过密钥将载体进行加密,最后使用…

ElasticSearch启动报错:Exception in thread “main“ SettingsException

Exception in thread "main" SettingsException[Failed to load settings from [elasticsearch.yml]]; nested: ParsingException[Failed to parse object: expecting token of type [START_OBJECT] but found [VALUE_STRING]]; 这个报错说明elasticsearch.yml这个配…

如何在VS Code上搭建 C/C++开发环境

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、什么是VScode VScode&#xff08;Visual Studio Code&#xff09;是一款由微软开发的免费开源的轻量级代码编辑器。它…

机器视觉学习(七)—— 卷积、边缘和滤波器

目录 一、卷积运算 1.1 卷积运算的公式 1.2 卷积操作 二、垂直边缘与水平边缘 2.1 cv2.filter2D()函数 2.2 Sobel算子 三、滤波器 一、卷积运算 1.1 卷积运算的公式 卷积运算是一种图像处理的基本操作&#xff0c;常用于图像滤波、边缘检测等应用中。 卷积运算的基本思…

【Linux】 centos7安装卸载SQL server(2017、2019)

一、安装配置 准备一个基础Linux配置&#xff1a; 内存为20GB 运行内存为2GB的系统&#xff08;数据库小于2GB安装不了&#xff09; 1、网络配置 我们需要进行网络的连接 进入 cd /ect/sysconfig/network-script/ 编辑文件ifcfg-ens33 vi ifcfg-ens33 Insert键进行编辑 把ONBOO…

公平锁和非公平锁,为什么要“非公平”?

公平锁和非公平锁&#xff0c;为什么要“非公平”&#xff1f; 主要讲一讲公平锁和非公平锁&#xff0c;以及为什么要“非公平”&#xff1f; 什么是公平和非公平 首先&#xff0c;我们来看下什么是公平锁和非公平锁&#xff0c;公平锁指的是按照线程请求的顺序&#xff0c;…

后端常问面经之Java基础

基本数据类型 Java中有8种基本数据类型&#xff1a; 6种数字类型&#xff1a; 4种整数型&#xff1a;byte、short、int、long 2种浮点型&#xff1a;float、double 1种字符类型&#xff1a;char 1种布尔类型&#xff1a;boolean 数据类型的默认值以及所占空间如下&#x…

java智慧城管执法平台源码,现场移动执法APP源码

智慧城管执法平台源码 智慧城管综合执法办案系统&#xff0c;提供了案件在线办理、当事人信用管理、文书电子送达、沿街店铺分析等功能&#xff0c;全面赋能执法队员&#xff0c;提高执法队员办案效率。 智慧城管综合执法办案系统在业务上能够支持所有行政处罚权力项目的网上…

一文整合工厂模式、模板模式、策略模式

为什么使用设计模式 今天终于有时间系统的整理一下这几个设计模式了&#xff0c; 这几个真是最常用的&#xff0c;用好了它们&#xff0c;你就在也不用一大堆的if else 了。能更好的处理大量的代码冗余问题。 在我们的实际开发中&#xff0c;肯定会有这样的场景&#xff1a;我…

High 级别反射型 XSS 攻击演示(附链接)

环境准备 如何搭建 DVWA 靶场保姆级教程&#xff08;附链接&#xff09;https://eclecticism.blog.csdn.net/article/details/135834194?spm1001.2014.3001.5502 测试 打开靶场找到该漏洞页面 先右键检查输入框属性 还是和之前一样的&#xff0c;所以直接输入 HTML 标签提交…