当前位置: 首页 > news >正文

DS-红黑树(RBTree)

一.红黑树

1.1 红黑树的起源

当对对AVL树做一些结构修改的操作时候,性能较为低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。
因此1972年Rudolf Bayer提出的对称二叉B树(Symmetric Binary B-Trees),随后在1978年Leo J. Guibas和Robert Sedgewick的工作中进一步发展和完善,最终形成了现代意义上的红黑树,它通过简单的规则和较少的旋转操作实现了有效的自平衡,广泛应用于各类需要高效查找、插入和删除操作的场合,例如在Java集合框架中的TreeMap和TreeSet类,以及哈希表中解决冲突时采用的链表+红黑树混合结构。

1.2 红黑树的概念

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

红黑树具有以下性质:

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

1.3 红黑树节点的定义

enum Color
{RED,BLACK
};template<typename T>
struct RBTreeNode
{RBTreeNode(const T& data): _left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED){}RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent; //父节点T _data;Color _col; //颜色
};

1.4 红黑树的插入

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

  1. 按照二叉搜索的树规则插入新节点。
  2. 检测新节点插入后,红黑树的性质是否造到破坏。
bool insert(const T& data)
{if (_root == nullptr){_root = new Node(data);return true;}Node* grandparent = nullptr;Node* uncle = nullptr;Node* parent = nullptr;Node* cur = _root;while (cur){parent = cur;if (data < cur->_data){cur = cur->_left;}else if (data > cur->_data){cur = cur->_right;}else{return false;}}cur = new Node(data);if (data < parent->_data){parent->_left = cur;}else if (data > parent->_data){parent->_right = cur;}else{assert(false);}cur->_parent = parent;while (parent && parent->_col == RED){grandparent = parent->_parent;if (grandparent){if (parent == grandparent->_left){uncle = grandparent->_right;}else{uncle = grandparent->_left;}}else{break;}if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandparent->_col = RED;cur = grandparent;}else{cur = Rotate(grandparent, parent, cur);}parent = cur->_parent;_root->_col = BLACK;}return true;
}

大致思路如下:
以下简称插入节点为cur,cur的父节点为parent,parent的父节点为grandparent,parent的兄弟节点为uncle。

  1. 先先按照二叉搜索树的规则将节点插入到红黑树中,节点颜色为红色。
  2. 插入后,红黑树的性质可能遭到破坏,此时就要根据红黑树的性质进行检测。
  3. cur插入后,如果parent颜色为黑色,则没有破坏红黑树的性质,插入结束。
  4. 若parent为红色,则此时有两种情况(uncle为红,uncle为黑/uncle不存在)
    1. uncle为红时,将parent和uncle变为黑色,grandparent变为红色,然后把grandparent视为cur,继续向上调整。
    2. uncle为黑/uncle不存在时,进行旋转。 (旋转在1.5处详细解释)

1.5 红黑树的旋转

此处的旋转与AVL树的旋转思路较为相似。

当uncle为黑/uncle不存在时,parent为cur的(左/右)孩子且parent为grandparent的(左/右)孩子,进行(右单旋)/(左单旋)。
右单旋

void RotateR(Node* parent)
{Node* ppnode = parent->_parent;Node* cur = parent->_left;Node* cur_right = cur->_right;if (ppnode){if (ppnode->_left == parent){ppnode->_left = cur;}else if (ppnode->_right == parent){ppnode->_right = cur;}else{assert(false);}}else{_root = cur;}cur->_parent = ppnode;parent->_left = cur_right;if (cur_right){cur_right->_parent = parent;}cur->_right = parent;parent->_parent = cur;
}

左单旋

void RotateL(Node* parent)
{Node* ppnode = parent->_parent;Node* cur = parent->_right;Node* cur_left = cur->_left;if (ppnode){if (ppnode->_right == parent){ppnode->_right = cur;}else if (ppnode->_left == parent){ppnode->_left = cur;}else{assert(false);}}else{_root = cur;}cur->_parent = ppnode;parent->_right = cur_left;if (cur_left){cur_left->_parent = parent;}cur->_left = parent;parent->_parent = cur;
}

当uncle为黑/uncle不存在时,parent为cur的(右/左)孩子且parent为grandparent的(左/右)孩子,进行(右单旋)/(左单旋)。

Node* Rotate(Node* grandparent, Node* parent, Node* cur)
{if (parent == grandparent->_left){if (cur == parent->_left){RotateR(grandparent);parent->_col = RED;grandparent->_col = BLACK;cur->_col = BLACK;return parent;}else{RotateL(parent);RotateR(grandparent);parent->_col = BLACK;grandparent->_col = BLACK;cur->_col = RED;return cur;}}else{if (cur == parent->_left){RotateR(parent);RotateL(grandparent);parent->_col = BLACK;grandparent->_col = BLACK;cur->_col = RED;return cur;}else{RotateL(grandparent);parent->_col = RED;grandparent->_col = BLACK;cur->_col = BLACK;return parent;}}

1.6 红黑树的特点与应用

  1. 红黑树是一棵不追求绝对平衡的二叉搜索树,其只需保证最长路径不超过最短路径的2倍,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优。
  2. 红黑树在C++ STL库中 map/set 等结构中充当底层结构,据说在java中哈希表中的哈希桶下的链表长度超过一定的阈值时,也会转换为红黑树提高效率。使得在处理大量冲突键时,极大的缓解了链表过长导致的哈希表查找效率退化。

1.7 完整代码

#pragma once
#include <iostream>
#include <assert.h>
using namespace std;enum Color
{RED,BLACK
};template<typename T>
struct RBTreeNode
{RBTreeNode(const T& data): _left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED){}RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Color _col;
};template<typename T>
class RBTree
{
public:typedef RBTreeNode<T> Node;RBTree():_root(nullptr){}bool insert(const T& data){if (_root == nullptr){_root = new Node(data);return true;}Node* grandparent = nullptr;Node* uncle = nullptr;Node* parent = nullptr;Node* cur = _root;while (cur){parent = cur;if (data < cur->_data){cur = cur->_left;}else if (data > cur->_data){cur = cur->_right;}else{return false;}}cur = new Node(data);if (data < parent->_data){parent->_left = cur;}else if (data > parent->_data){parent->_right = cur;}else{assert(false);}cur->_parent = parent;while (parent && parent->_col == RED){grandparent = parent->_parent;if (grandparent){if (parent == grandparent->_left){uncle = grandparent->_right;}else{uncle = grandparent->_left;}}else{break;}if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandparent->_col = RED;cur = grandparent;}else{cur = Rotate(grandparent, parent, cur);}parent = cur->_parent;_root->_col = BLACK;}return true;}Node* get_Root(){return _root;}bool checkColour(Node* root, int blacknum, int benchmark){if (root == nullptr){if (blacknum != benchmark)return false;return true;}if (root->_col == BLACK){++blacknum;}if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << root->_data << "出现连续红色节点" << endl;return false;}return checkColour(root->_left, blacknum, benchmark)&& checkColour(root->_right, blacknum, benchmark);}bool isBalance(){return isBalance(_root);}int height(){return height(_root);}
private:Node* _root;void RotateR(Node* parent){Node* ppnode = parent->_parent;Node* cur = parent->_left;Node* cur_right = cur->_right;if (ppnode){if (ppnode->_left == parent){ppnode->_left = cur;}else if (ppnode->_right == parent){ppnode->_right = cur;}else{assert(false);}}else{_root = cur;}cur->_parent = ppnode;parent->_left = cur_right;if (cur_right){cur_right->_parent = parent;}cur->_right = parent;parent->_parent = cur;}void RotateL(Node* parent){Node* ppnode = parent->_parent;Node* cur = parent->_right;Node* cur_left = cur->_left;if (ppnode){if (ppnode->_right == parent){ppnode->_right = cur;}else if (ppnode->_left == parent){ppnode->_left = cur;}else{assert(false);}}else{_root = cur;}cur->_parent = ppnode;parent->_right = cur_left;if (cur_left){cur_left->_parent = parent;}cur->_left = parent;parent->_parent = cur;}Node* Rotate(Node* grandparent, Node* parent, Node* cur){if (parent == grandparent->_left){if (cur == parent->_left){RotateR(grandparent);parent->_col = RED;grandparent->_col = BLACK;cur->_col = BLACK;return parent;}else{RotateL(parent);RotateR(grandparent);parent->_col = BLACK;grandparent->_col = BLACK;cur->_col = RED;return cur;}}else{if (cur == parent->_left){RotateR(parent);RotateL(grandparent);parent->_col = BLACK;grandparent->_col = BLACK;cur->_col = RED;return cur;}else{RotateL(grandparent);parent->_col = RED;grandparent->_col = BLACK;cur->_col = BLACK;return parent;}}}bool isBalance(Node* root){if (root == nullptr)return true;if (root->_col != BLACK){return false;}// 基准值int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)++benchmark;cur = cur->_left;}return checkColour(root, 0, benchmark);}int height(Node* root){if (root == nullptr)return 0;int leftHeight = height(root->_left);int rightHeight = height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}
};

————————————————————
感谢大家观看,不妨点赞支持一下吧[doge]
如有错误,随时纠正,谢谢大家

相关文章:

DS-红黑树(RBTree)

一.红黑树 1.1 红黑树的起源 当对对AVL树做一些结构修改的操作时候&#xff0c;性能较为低下&#xff0c;比如&#xff1a;插入时要维护其绝对平衡&#xff0c;旋转的次数比较多&#xff0c;更差的是在删除时&#xff0c;有可能一直要让旋转持续到根的位置。 因此1972年Rudolf…...

ubuntu 如何使用阿里云盘

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…...

sqlite3 交叉编译

#1.下载源码并解压 源码路径如下&#xff0c;下载autoconf版本 SQLite Download Page 解压 tar -zxvf sqlite-autoconf-3450200.tar.gz cd sqlite-autoconf-3450200 mkdir build # 2. 配置源代码 # 假设你已经安装了交叉编译工具链&#xff0c;如gcc-arm-linux-gnueabih…...

【AI生成文章】flutter ChangeNotifierProvider 实用场景举例

内容由Ai 大模型生成&#xff0c;不能完全保障真实 ChangeNotifierProvider 是 Flutter 中一个非常实用的工具&#xff0c;用于在应用程序中管理和传递状态。以下是一些实用的场景举例&#xff1a; 1. 用户信息管理 在应用程序中&#xff0c;用户信息&#xff08;如用户名、…...

【0274】从shared init file或local init file加载relation cache(2 - 1)

上一篇: 【0273】深入分析 relcache(relation descriptor cache)初始化第一阶段(1) 【0264】深入分析relcache(relation descriptor cache)缓存初始化第2阶段(2) 1. 前言 本文内容是作为《【0264】深入分析relcache(relation descriptor cache)缓存初始化第2阶段…...

蓝桥杯-02-2023蓝桥杯c/c++省赛B组题目

参考 2023 年第十四届蓝桥杯 C/C B组省赛题解 2023蓝桥杯c/c省赛B组题目(最全版)&#xff1a; A&#xff1a;日期统计 这题方法应该很多&#xff0c;没有和别人讨论想法。我的解法思路是&#xff1a;先 load 函数生成所有这一年的合法日期&#xff0c;然后枚举所有可以从数据…...

欧拉筛+并查集

集合 - 洛谷 std::vector<int> minp, primes,primes1;void sieve(int n,int p) {minp.assign(n 1, 0);primes.clear();for (int i 2; i < n; i) {if (minp[i] 0) {minp[i] i;primes.push_back(i);}for (auto p : primes) {if (i * p > n) {break;}minp[i * p]…...

《桥接模式(极简c++)》

本文章属于专栏《设计模式&#xff08;极简c版&#xff09;》 继续上一篇《原型模式&#xff08;极简c&#xff09;》。本章简要说明桥接模式。本文分为模式说明、本质思想、实践建议、代码示例四个部分。 模式说明 方案&#xff1a; 将抽象部分与它的实现部分分离&#xff0c…...

jconsole的使用

前提 已安装jdk 使用步骤 1、命令行输入jconsole...

JavaScript详细教程

文章目录 前言一、代码位置二、注释三、变量1.字符串类型2.数组3.对象&#xff08;字典&#xff09; 四、条件语句五、函数六、DOM模板 前言 JavaScript 是一种脚本编程语言&#xff0c;它可以在网页上实现复杂的功能&#xff0c;网页展现给你的不再是简单的静态信息&#xff0…...

Hive自定义GenericUDF函数

Hive自定义GenericUDF函数 当创建自定义函数时&#xff0c;推荐使用 GenericUDF 类而不是 UDF 类&#xff0c;因为 GenericUDF 提供了更灵活的功能和更好的性能。以下是使用 GenericUDF 类创建自定义函数的步骤&#xff1a; 编写Java函数逻辑&#xff1a;编写继承自 GenericUDF…...

伊理威科技:抖音开网店新手刚做选啥品

在数字浪潮中&#xff0c;抖音不仅是展示才艺的舞台&#xff0c;更是创业者的新天地。新手若想在这片热土上开垦网店&#xff0c;选品便是首要课题。选择产品如同种下希望的种子&#xff0c;既要考量土壤肥沃度&#xff0c;也得预测风雨适宜期。 兴趣与专长是选品的罗盘。热爱所…...

【爬虫】专栏文章索引

为了方便 快速定位 和 便于文章间的相互引用等 作为一个快速准确的导航工具 爬虫 目录&#xff1a; &#xff08;一&#xff09;web自动化和接口自动化 &#xff08;二&#xff09;实战-爬取Boss直聘信息数据...

【Linux】Linux开发工具-vim / 编译器-gcc/g++ / 调试器-gdb / git操作 / 项目自动化构建工具-make/Makefile

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;Linux_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.在Linux写自己的第一个程序 1.1 nano指令 1.2 nano指令的使用 1.2.1 介绍 1.2.2 演示 1.2.2.1 创建.c文件 1.2.2.2 nano cod…...

解决VM重新打开后找不到共享文件夹的问题

我的问题是之前按照网上的文档设置了vm的共享文件夹&#xff0c;能成功使用&#xff0c;但是问题是下一次打开之后就找不到了&#xff0c;虚拟机设置里共享文件夹是启用的&#xff0c;文件夹也完成了映射网络驱动器&#xff0c;但是就是找不到共享文件夹 解决方法&#xff1a;…...

uni app 空挡接龙

pc游戏 空挡接龙 还不完整。现在没时间搞了记录在这里&#xff0c;等以后有时间了再继续搞。 <template><view class"page_main"><view class"contentone"><canvas class"canvas_cla" style"z-index: 1;" canva…...

oracle表备份及还原

工作中&#xff0c;经常使用Navicat访问及操作Oracle数据库&#xff0c;备份表非常方便Ctrlc、Ctrlv&#xff1b;最近备份表&#xff0c;发现这种操作有问题&#xff1b;数据表有2条检查&#xff0c;使用Ctrlc、Ctrlv操作&#xff0c;发现新备份的表出现4条检查&#xff0c;再对…...

牛客小白月赛89补题1(ABCD)(偏难)

评价&#xff1a; 高情商&#xff1a;收获很大 &#xff0c;让自己进一步认清自己。 低情商&#xff1a;题目难&#xff0c;自己太菜了。 今天还有一些其他事&#xff0c;剩下的题明天再补。 我们从a题开始吧&#xff1a; A.签到 我们只要看看其中的max与min是否不符合即可…...

内存条@电脑支持的最大内存@升级内存硬件

文章目录 电脑支持的最大内存规格cpu官网查看支持的规格命令行查看脚本化 DDR内存LPDDR内存内存升级扩展&#x1f47a;插槽检查板载内存SPD内存厂商其他 内存参数&#x1f47a;性能指标使用软件查看更多内存相关的软件工具 电脑支持的最大内存规格 确认电脑最大支持内存大小和频…...

如何了解AI基础概念

1. **在线课程和教程&#xff1a;** - 寻找在线AI课程或教程&#xff0c;例如Coursera、edX、Udemy等平台上的课程。这些课程通常会从基础概念开始介绍&#xff0c;逐步深入。 2. **书籍阅读&#xff1a;** - 阅读与AI相关的书籍&#xff0c;如《Python深度学习》、《机…...

KKS-HF Patch 完整解决方案:优化《Koikatsu Sunshine》游戏体验指南

KKS-HF Patch 完整解决方案&#xff1a;优化《Koikatsu Sunshine》游戏体验指南 【免费下载链接】KKS-HF_Patch Automatically translate, uncensor and update Koikatsu Sunshine! 项目地址: https://gitcode.com/gh_mirrors/kk/KKS-HF_Patch KKS-HF Patch 是针对《Koi…...

浏览器Markdown渲染工具完全指南:解决本地文件预览难题

浏览器Markdown渲染工具完全指南&#xff1a;解决本地文件预览难题 【免费下载链接】markdown-viewer Markdown Viewer / Browser Extension 项目地址: https://gitcode.com/gh_mirrors/ma/markdown-viewer 为什么专业人士需要专用的Markdown预览方案&#xff1f; 技术…...

互联网大厂Java求职者面试实录:严肃面试官VS搞笑水货程序员小李

互联网大厂Java求职者面试实录&#xff1a;严肃面试官VS搞笑水货程序员小李 第一轮提问&#xff1a;Java基础与多线程 面试官&#xff1a;小李&#xff0c;Java中HashMap的工作原理是什么&#xff1f;当多线程并发访问时会出现什么问题&#xff1f; 小李&#xff1a;HashMap就是…...

Git新手必看:5个最常用命令搞定代码拉取与分支管理(附Gerrit实战)

Git新手必看&#xff1a;5个最常用命令搞定代码拉取与分支管理&#xff08;附Gerrit实战&#xff09; 第一次接触Git时&#xff0c;面对满屏的命令行参数和陌生的概念&#xff0c;很多开发者都会感到手足无措。记得我刚入职时&#xff0c;为了提交一行代码修改&#xff0c;整整…...

OpenClaw技能组合策略:千问3.5-35B-A3B-FP8驱动复杂工作流5个案例

OpenClaw技能组合策略&#xff1a;千问3.5-35B-A3B-FP8驱动复杂工作流5个案例 1. 为什么需要技能组合&#xff1f; 去年我尝试用单一技能处理竞品分析时&#xff0c;发现模型生成的报告总是缺少关键数据支撑。当我手动补充爬虫结果后&#xff0c;又面临图表生成与多语言翻译的…...

无线网卡选购指南:别再被商家忽悠了,这5个参数才是关键

无线网卡选购指南&#xff1a;别再被商家忽悠了&#xff0c;这5个参数才是关键本文为付费专栏内容&#xff0c;全文约3800字&#xff0c;阅读需12分钟 适合人群&#xff1a;台式机用户、老旧笔记本用户、游戏玩家、NAS玩家前言&#xff1a;为什么你需要单独买无线网卡&#xff…...

8大AI核心概念,让你秒懂智能体、多智能体系统、RAG、工作流、微调、函数调用、MCP和A2A!

本文介绍了8个AI核心概念&#xff0c;包括智能体&#xff08;Agent&#xff09;和多智能体系统&#xff08;Multi-Agent System&#xff09;&#xff0c;以及如何通过RAG&#xff08;Retrieval-Augmented Generation&#xff09;、工作流&#xff08;Work Flow&#xff09;、微…...

如何用双路PWM实现16bit DAC输出?MCU音频信号处理实战

如何用双路PWM实现16bit DAC输出&#xff1f;MCU音频信号处理实战 在嵌入式音频开发中&#xff0c;高精度DAC输出往往是提升音质的关键。但当你手头的MCU主频有限&#xff0c;内置DAC分辨率不足时&#xff0c;如何突破硬件限制&#xff1f;本文将带你深入双路PWM分频叠加技术的…...

OpenClaw+千问3.5-35B-A3B-FP8:自媒体图文内容自动化生产

OpenClaw千问3.5-35B-A3B-FP8&#xff1a;自媒体图文内容自动化生产 1. 为什么选择自动化内容生产 作为一个长期运营技术自媒体的创作者&#xff0c;我每天需要花费大量时间在内容生产上&#xff1a;从选题策划、素材收集、文案撰写到排版发布&#xff0c;整个过程往往需要4-…...

新手也能搞定的应急响应实战:用知攻善防靶场复现近源渗透与挖矿事件

新手也能搞定的应急响应实战&#xff1a;用知攻善防靶场复现近源渗透与挖矿事件 网络安全应急响应是每个安全从业者的必修课&#xff0c;但对于刚入门的新手来说&#xff0c;面对真实的攻击事件往往无从下手。本文将带你通过知攻善防靶场&#xff0c;手把手复现"近源渗透O…...