数据结构·红黑树

1. 红黑树的概念

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

        红黑树的平衡不像是AVL树那样的绝对平衡,红黑树是一种近似平衡,它避免了处理繁琐的平衡因子,同时它搜索的效率也基本没有影响,不过是最坏情况要搜索 2*logN 但是这从时间复杂度上看,与AVL树的绝对logN是一样的。

2. 红黑树的规则

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

        2. 根节点是黑色的

        3. 如果一个节点是红色的,则它的两个孩子节点是黑色的 (没有两个连续的红节点)

        4. 对于每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点

        5. 每个叶子节点下的空节点都是黑色的,我们管这类空节点叫NIL节点,主要是用来统计总路径用的

        只要满足前4条规则就可以满足红黑树中:“最长路径中节点个数不会超过最短路径节点个数的两倍” 的原则。在我们写树,或者说对树进行调整的时候都不需要考虑规则5,规则5就是个计数用的

3. 红黑树的节点

                

        红黑树节点的颜色我们默认给红色。

        因为如果默认给黑色的话就会导致每新增一个节点时,该路径的黑色节点就会比别的路径多一个,此时不满足规则4,就要对树进行调整。

        但是如果插入的是一个红色节点,如果它的父节点是黑色的,则不用对树进行任何调整,只有它的父节点是红色的,才会违背规则3,需要对树进行调整。

4. 红黑树的插入

        红黑树的在插入时要一直符合那4个规则,因此在插入之后有两种调整方案。1. 更新节点的颜色,2. 当更新节点颜色的时候也控制不住这个树了的话就对树进行旋转。

        经过总结和推到给出的结果就是:当我们在调整树的时候只需要关注4个节点的状态

                

        分别是当前节点cur,父节点(parent) p,祖父节点(grandparent) g,叔叔节点(uncle) u。

        具体到选择那种调整方案的时候只需要看 uncle节点的状态,如果是红色就选第一种更新方案,如果是黑色或者不存在就选第二种更新方案 

        第一种更新方案是只需要进行颜色的更新就可以维持住红黑树的规则。

        第二种更新方案是需要旋转才可以。第二种更新方案又分为单旋和双旋两种情况

4.1 当uncle节点为红色时

        cur节点是新插入的,或者是调整上来的当前需要调整的节点,它一定是红色的,因为如果是黑色就不必调整了,也不会走到这步。

        parent节点一定是红色的,因为如果parent节点是黑色的话我们就不用继续调整了,可以跳出调整循环了。

        grandparent节点一定是黑色的,因为如果它是红色的,说明这棵树之前就该调整了,p和g两个红色节点早就连到一块了,或者说这棵树已经坏了。

        那么此时我们假设uncle节点为红色,此时的调整思路如下图

        我们将g节点的黑色状态下发给p和u两个节点,再将g节点颜色改为红色,此时没有在任何路径上新增黑色节点,不会破坏规则4,同时也维护住了规则3。

        然后我们以g节点为cur,继续向上更新,此时更新分3种情况

        如果g节点已经是根了,那我们就不能往上更新了,然后将g节点也就是根节点置黑。

        如果g节点的parent存在且是黑色,那就不用继续更新了。

        如果g节点的parent存在且是红色,就需要向上更新

4.2 当uncle节点为黑或不存在时

        此时的树已经无法通过更新节点颜色控制住的了,因此我们要开始旋转树。

        与AVL树判断单双旋的思路是一样的,如果g、p、cur三个节点在同一边就进行单旋;如果一个在left一个在right,也就是说3个节点不在同一边就进行双旋。

4.1 单旋情况

        当uncle节点不存在时cur一定是新增的节点,因为如果unlce不存在,也就是说以g为根的路径上只能有g节点一个黑色节点,那a、b、c树如果存在的话它们的节点又应该是什么颜色的呢?都是红色吗?因此a、b、c树一定不存在,也就是说cur一定是新增节点。

        那如果uncle不存在的情况能不能通过更新颜色来进行规则控制呢?比如把p节点置黑,g节点置红,然后再向上更新。答案也是不行的。可以想象,如果按照这个方案执行的话,如果插入一个有序的数组,岂不是又变成了一个单支树了。

        我们回到单旋的操作,一共分成两步。1. 以g为根进行单旋。2.处理颜色。但是旋完之后我们不需要进行向上更新了,因为旋完的根p是一个黑色的节点。

4.2 双旋情况

        当g、p、cur三个节点不在同一边就要进行双旋

        先单旋p节点变成上面的单旋情况,然后再单旋,最后更改颜色。

5. 红黑树与AVL树的比较

        红黑树和AVL树都是高效的平衡二叉树,增删查改的时间复杂度都是O(logN),红黑树不追求绝对的平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中红黑树性能更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

6. 红黑树完整代码

        红黑树的删除代码也先欠着

//节点的颜色
enum color{RED,BLACK};
//红黑树节点的定义
template<class K, class V>
struct RBTNode
{RBTNode(const pair<K, V>& kv, color color = RED): _kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _col(color) {}pair<K, V> _kv;RBTNode<K, V>* _left;RBTNode<K, V>* _right;RBTNode<K, V>* _parent;color _col;	//节点颜色
};template<class K, class V>
class RBTree
{typedef RBTNode<K, V> Node;public://构造RBTree() = default;//拷贝构造RBTree(const RBTree<K, V>& t){_root = Copy(t._root);}//赋值运算符重载void operator=(const RBTree<K, V>& t){RBTree<K, V> new_t(t);std::swap(new_t._root, _root);}//析构~RBTree(){Destroy(_root);_root = nullptr;}void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;}Node* Copy(const Node* root){if (root == nullptr)return nullptr;Node* newnode = new Node(root->kv);newnode->_left = Copy(root->_left);newnode->_right = Copy(root->_right);return newnode;}//插入bool Insert(const pair<K, V>& kv);//搜索Node* Find(const K& x);//中序遍历void InOrder(){_InOrder(_root);cout << endl;}//树的高度int Height(){return _Height(_root);}//统计节点总个数(插入时可能会有重复数据)int Size(){return _Size(_root);}private://左单旋void RotateL(Node* parent);//右单旋void RotateR(Node* parent);//中序遍历(子函数)void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}//树的高度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;}//统计节点总个数(插入时可能会有重复数据)int _Size(Node* root){return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;}private:Node* _root = nullptr;
};//插入
template<class K, class V>
bool RBTree<K, V>::Insert(const pair<K, V>& kv)
{//链表为空特殊处理if (_root == nullptr){_root = new Node(kv, 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;}elsereturn false;}//需要插入一个新的红色节点cur = new Node(kv);if (cur->_kv.first < parent->_kv.first){parent->_left = cur;cur->_parent = parent;}else{parent->_right = cur;cur->_parent = parent;}//判断是否需要对树进行调整while (parent && parent->_col==RED)//如果父节点颜色为红需要调整{Node* grandparent = parent->_parent;if (parent == grandparent->_left)	//叔叔在右{Node* uncle = grandparent->_right;if (uncle && uncle->_col == RED)	//当叔叔存在且为红时{parent->_col = uncle->_col = BLACK;grandparent->_col = RED;cur = grandparent;parent = grandparent->_parent;}else	//叔叔为黑或不存在{if (cur == parent->_left)//三节点同在左,右单旋{RotateR(grandparent);parent->_col = BLACK;grandparent->_col = RED;}else	//p节点在g左,cue节点在p右,左右双旋{RotateL(parent);RotateR(grandparent);cur->_col = BLACK;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 = grandparent->_parent;}else	//叔叔为黑或不存在{if (cur == parent->_right)//三节点同在右,左单旋{RotateL(grandparent);parent->_col = BLACK;grandparent->_col = RED;}else	//p节点在g右,cue节点在p左,右左双旋{RotateR(parent);RotateL(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}}_root->_col = BLACK;return true;
}//搜索
template<class K, class V>
RBTNode<K, V>* RBTree<K, V>::Find(const K& x)
{Node* cur = _root;while (cur){if (cur->_kv.first < x){cur = cur->_right;}else if (cur->_kv.first > x){cur = cur->_left;}else{return cur;}}return nullptr;
}//左单旋
template<class K, class V>
void RBTree<K, V>::RotateL(Node* parent)
{Node* subR = parent->_right;Node* subRL = parent->_right->_left;//修改向下链接内容parent->_right = subRL;subR->_left = parent;//修改向上链接内容subR->_parent = parent->_parent;parent->_parent = subR;if (subRL)//防止该树点为空{subRL->_parent = parent;}//parent的parent向下链接Node* parentParent = subR->_parent;if (parentParent == nullptr)//整棵树的根{_root = subR;}else{if (parent == parentParent->_right){parentParent->_right = subR;}else{parentParent->_left = subR;}}
}//右单旋
template<class K, class V>
void RBTree<K, V>::RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;//修改向下链接内容parent->_left = subLR;subL->_right = parent;//修改向上链接属性subL->_parent = parent->_parent;parent->_parent = subL;if (subLR){subLR->_parent = parent;}//修改parentParentNode* parentParent = subL->_parent;if (parentParent == nullptr){_root = subL;}else{if (parent == parentParent->_right){parentParent->_right = subL;}else{parentParent->_left = subL;}}
}

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

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

相关文章

秋招突击——7/29——复习{有塔游戏——关联传递性}——新作{随机链表的复制、合并K个升序链表,二叉树——二叉树的中序遍历、二叉树的最大深度、反转二叉树}

文章目录 引言复习有塔游戏——关联传递性实现复习实现参考实现 新作随机链表的复制个人实现参考实现 排序链表个人实现参考实现 二叉树章节二叉树的中序遍历个人实现 二叉树的最大深度个人实现参考实现 反转二叉树个人实现参考实现 总结 引言 旅游完回来了&#xff0c;今天继…

Matlab编程资源库(14)常微分方程初值问题的数值解法

一、 龙格&#xff0d;库塔法简介 龙格-库塔法&#xff08;Runge-Kutta method&#xff09;是一种常用的数值解微分方程的方法&#xff0c;由德国数学家卡尔龙格&#xff08;Carl Runge&#xff09;和马丁威尔海尔姆库塔&#xff08;Martin Wilhelm Kutta&#xff09;在20世纪…

IDEA 本地有jar包依赖文件,但是所有引用的jar包全部爆红

前端时间 看源码&#xff0c;下载源码额按钮不见了&#xff0c;折腾了很久&#xff0c;遂打算重新安装idea&#xff0c;但是重新安装后&#xff0c;发现代码全都爆红&#xff0c;按照晚上说的删除idea 文件夹&#xff0c;idea缓存删除&#xff0c;都不好使&#xff0c;但是看到…

【JavaScript】`Map` 数据结构

文章目录 一、Map 的基本概念二、常见操作三、与对象的对比四、实际应用场景 在现代 JavaScript 中&#xff0c;Map 是一种非常重要且强大的数据结构。与传统的对象&#xff08;Object&#xff09;不同&#xff0c;Map 允许您使用各种类型的值作为键&#xff0c;不限于字符串或…

机器学习算法——常规算法,在同的业务场景也需要使用不同的算法(一)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

【Vulnhub系列】Vulnhub_SecureCode1靶场渗透(原创)

【Vulnhub系列靶场】Vulnhub_SecureCode1靶场渗透 原文转载已经过授权 原文链接&#xff1a;Lusen的小窝 - 学无止尽&#xff0c;不进则退 (lusensec.github.io) 一、环境配置 1、从百度网盘下载对应靶机的.ova镜像 2、在VM中选择【打开】该.ova 3、选择存储路径&#xff0…

“数说”巴黎奥运会上的“中国智造”成果

引言&#xff1a;随着“中国智造”在欧洲杯上方兴未艾&#xff0c;在巴黎奥运会上&#xff0c;中国智造继续以多种形式和领域展现了其强大的实力和创新能力。以格力公开表示将为巴黎奥运村提供345台格力空调&#xff0c;为中国制造的清凉送至巴黎事件拉开中国制造闪亮巴黎奥运会…

浅谈取样器之调试取样器

浅谈取样器之调试取样器 JMeter的调试取样器(Debug Sampler)是一个非常实用的工具&#xff0c;它帮助用户在测试计划执行过程中获取详细的内部状态信息&#xff0c;这对于诊断脚本错误、理解变量作用域、以及确认配置是否按预期工作至关重要。调试取样器可以显示JMeter变量、属…

将gitee 上的nvim 配置 从gitee 上下载下来,并配置虚拟机

首先是下载 gitee 上的配置。 然后是 配置 tmux 然后是配置nvim . 1 在init.lua 文件中注释掉所有的与第三方插件有关的内容。 2 在packer 的文件中 &#xff0c; 注释掉所有的与 第三方插件有关的代码。 3 首先要保证 packer 能够正确的安装。 4 然后开始 安装 所有的插件…

【SOC 芯片设计 DFT 学习专栏 -- DFT DRC规则检查】

请阅读【嵌入式及芯片开发学必备专栏】 请阅读【芯片设计 DFT 学习系列 】 如有侵权&#xff0c;请联系删除 转自&#xff1a; 芯爵ChipLord 2024年07月10日 12:00 浙江 文章目录 概述DRC的概念Tessent DRC检查的概述时钟相关检查扫描相关检查BIST规则检查预DFT时钟规则检查 …

Git(分布式版本控制系统)(fourteen day)

一、分布式版本控制系统 1、Git概述 Git是一种分布式版本控制系统&#xff0c;用于跟踪和管理代码的变更&#xff0c;它由Linux、torvalds创建的&#xff0c;最初被设计用于Linux内核的开发。Git允许开发人员跟踪和管理代码的版本&#xff0c;并且可以在不同的开发人员之间进行…

链表篇-02.从尾到头打印链表(反转链表)

解题思路&#xff1a; 链表从尾到头打印链表, 我的思路是 用三个指针,第一个指针(pre)指向指向头节点的前一个位置&#xff0c;第二个指针(cur)指向头节点&#xff0c; 然后依次往后执行&#xff0c;第三个指针用于临时记录第二个指针的下一个位置。 代码详情: import java.…

【Code】Street-Gaussian代码复现笔记

文章目录 1. EnvironmentBug 1 2. TrainingBug 2Bug 3 1. Environment Follow the original instructions, conda create --name street-gaussians-ns -y python3.8 conda activate street-gaussians-ns pip install --upgrade pippip install torch2.1.2cu118 torchvision0.…

差分法求解 Burgers 方程(附完整MATLAB 及 Python代码)

Burgers 方程的数值解及误差分析 引言 Burgers 方程是一个非线性偏微分方程&#xff0c;在流体力学、非线性声学和交通流理论中有广泛应用。本文将通过数值方法求解带粘性的 Burgers 方程&#xff0c;并分析其误差。 方程模型 Burgers 方程的形式为&#xff1a; u t u u …

如何快速获取全网精准客流?揭秘不为人知的5大运营策略!

有同行所在的地方&#xff0c;就一定拥有咱们需要的客户。客户看的是结果&#xff0c;搜索的是问题&#xff0c;寻找的是答案。 如果没有付费流量&#xff0c;单纯靠搞免费流量&#xff0c;很多大厂的运营也会变得一文不值。一个牛逼的运营&#xff0c;不仅是会做付费流量&…

Sentinel隔离、降级、授权规则详解

文章目录 Feign整合Sentinel线程隔离熔断降级授权规则自定义异常结果 上一期教程讲解了 Sentinel 的限流规则&#xff1a; Sentinel限流规则&#xff0c;这一期主要讲述 Sentinel 的 隔离、降级和授权规则 虽然限流可以尽量避免因高并发而引起的服务故障&#xff0c;但服务还…

我们的前端开发逆天了!1 小时搞定了新网站,还跟我说 “不要钱”

大家好&#xff0c;我是程序员鱼皮。前段时间我们上线了一个新软件 剪切助手 &#xff0c;并且针对该项目做了一个官网&#xff1a; 很多同学表示官网很好看&#xff0c;还好奇是怎么做的&#xff0c;其实这个网站的背后还有个有趣的小故事。。。 鱼皮&#xff1a;我们要做个官…

Mastercam2020中文版安装教程许可证激活码教程附安装包【亲测成功】

软件简介 Mastercam是美国CNC Software Inc.公司开发的基于PC平台的CAD/CAM软件。它集二维绘图、三维实体造型、曲面设计、体素拼合、数控编程、刀具路径模拟及真实感模拟等多种功能于一身。它具有方便直观的几何造型。Mastercam提供了设计零件外形所需的理想环境&#xff0c;其…

Sonatype Nexus Repository搭建与使用(详细教程3.70.1)

目录 一. 环境准备 二. 安装jdk 三. 搭建Nexus存储库 四. 使用介绍 一. 环境准备 主机名IP系统软件版本配置信息nexus192.168.226.26Rocky_linux9.4 Nexus Repository 3.70.1 MySQL8.0 jdk-11.0.23 2核2G&#xff0c;磁盘20G 进行时间同步&#xff0c;关闭防火墙和selinux…

1.Redis介绍

redis是一个键值型数据库。 是一种nosql数据库&#xff0c;非关系型数据库。 sql数据库 1.字段类型是固定的。 2.表的结构是固定的。表数据量特别大的时候&#xff0c;去修改表结构会出现问题。也会导致业务逻辑的修改。 3.每个字段有一定的约束&#xff0c;比如唯一约束&…