【数据结构】红黑树——领略天才的想法

个人主页:东洛的克莱斯韦克-CSDN博客  

祝福语:愿你拥抱自由的风       

目录

二叉搜索树

AVL树

红黑树概述

性质详解

效率对比

旋转操作

元素操作

代码实现


二叉搜索树

【数据结构】二叉搜索树-CSDN博客

AVL树

【数据结构】AVL树——平衡二叉搜索树-CSDN博客

红黑树概述

概念

在二叉搜索树的基础上符合一下性质便是红黑树

每一个节点不是红色就是黑色

根节点一定是黑色

空节点一定是黑色

父亲节点和孩子节点不能是连续的红色节点

每一条根节点至空节点路径上的黑色节点的数量一定相等

性质详解

理解路径

红黑树中,一条完整路径不是从叶子节点溯源至根节点,而是从空节点溯源至根节点

理解最长和最短路径

如果全是黑色节点,红黑树就是一颗满二叉树,因为每条路径的黑色节点数量相等

那么这颗树的每条完整路径都是最短路径,

如果在一条路径上红黑节点间隔(不允许连续的有红色节点),那么该路径为最长路径。

红黑树规则

那么最长路径是最短路径的两倍。这便是红黑树的平衡规则。

满二叉树的平衡条件是左右子树高度差为0,AVL树的平衡条件是左右子树高度差小于等于 1,相比于前两棵树平衡条件,红黑树是一种弱平衡

红黑树和AVL树一样,只要保证自己的平衡规则不被打破,就能使自己不退化为类似于链表的结构。

退化成类似链表的结构——插入的数据接近有序或插入大量的数据不够随机或进行了一些插入删除等操作。

效率对比

查找效率

从直觉上讲,红黑树只是维持一种弱平衡,在最坏情况下,红黑树的高度是AVL树高度的两倍,那么红黑树查找数据的效率也应该比AVL树低两倍才对,为什么我们认为红黑树是一种更优的数据结构呢?下面小编带大家算笔账

2的40次方是一万亿,也就是说用满二叉树存一万亿个数据高度为40。AVL树是有高度差的,所以最坏情况下会查找40多次,而红黑树最坏情况下会找80多次。

那么对于cpu而言,找40多次和找80多次有区别吗?答案是没有的,现在的cpu每秒钟可以运算十亿次甚至几百亿。

可以理解为,在查找数据的效率上AVL树和红黑树是一样的。那么,红黑树的优势在哪里呢?

插入删除效率

不管是红黑树还是AVL树,如果打破平衡都需要旋转这一操作恢复平衡,旋转所付出的时间复杂度为O(1)。对于AVL树而言,需要溯源更新平衡因子,对于红黑树而言,需要溯源更新节点颜色,溯源更新最坏情况下是从叶子节点更新到根节点,所付出的时间复杂度为O(logN)

因为AVL树的高度差小于等于1,平衡很容易被打破,要维持平衡就需要不断地付出O(1)O(logN)来维持平衡。

那么红黑树维持弱平衡就不需要总是付出这样地代价,所以红黑树是一种更优的数据结构

旋转操作

旋转操作不是AVL树或红黑树特有的,旋转一次的本质是让二叉搜索树的某棵子树的高度减一

对于红黑树而言,最长路径是最短路径的二倍加一,就意味着打破平衡,需要通过旋转让最长路径上的某棵子树高度减一来恢复平衡。旋转后需要更新节点的颜色,具体要怎么控制颜色下面细讲,现在看一下旋转操作吧

左单旋:新节点插入较高右子树的右侧——对fathernode

void RevolveLeft(node *& fathernode)//左单旋      
{node* cur = fathernode->_right; //父亲节点的右孩子fathernode->_right = cur->_left; //更改指向关系if (cur->_left != nullptr) //特殊情况cur->_left->_FatherNode = fathernode;//更改指向关系cur->_FatherNode = fathernode->_FatherNode;//更改指向关系if (fathernode->_FatherNode != nullptr) //为空是特殊情况,{if (fathernode->_FatherNode->_right == fathernode){fathernode->_FatherNode->_right = cur;//更改指向关系}else{fathernode->_FatherNode->_left = cur;//更改指向关系}}cur->_left = fathernode;//更改指向关系fathernode->_FatherNode = cur;//更改指向关系}

右单旋:新节点插入较高左子树的左侧——对fathernode

void RevolveRight(node *& fathernode) //右单旋
{node* cur = fathernode->_left; //父亲节点的左节点fathernode->_left = cur->_right;//更新指向关系if (cur->_right != nullptr) //特殊情况cur->_right->_FatherNode = fathernode;//更新指向关系cur->_FatherNode = fathernode->_FatherNode;//更新指向关系if (fathernode->_FatherNode != nullptr)//特殊情况{if (fathernode->_FatherNode->_right == fathernode){fathernode->_FatherNode->_right = cur;//更新指向关系}else{fathernode->_FatherNode->_left = cur;//更新指向关系}}cur->_right = fathernode;//更新指向关系fathernode->_FatherNode = cur;//更新指向关系}

左右双旋:新节点插入在较高左子树的右侧——先对fathernodeL左单旋再对fathernodeLR右单旋

右左双旋:新节点插入再较高右子树的左侧——先对fathernodeR右单旋再对fathernodeRL左单旋

元素操作

我们再来理解一下红黑树两条核心性质

父亲节点和孩子节点不能是连续的红色节点

每一条根节点至空节点路径上的黑色节点的数量一定相等

红黑树插入的新节点应该是黑色还是红色呢?

也就是说,插入红色节点可能会破坏第一条性质,插入黑色节点会破坏第二条性质。第一条性质被破坏只影响了当前路径,而第二条性质被破坏影响的是所有路径。所以插入新节点的颜色是红色

如果新插入节点的父亲节点是黑色,那么不会破坏任何性质,如果新插入节点的父亲节点是红色该怎么办呢?

颜色管理

下面介绍红黑树如何通过管理颜色来判断是否需要旋转,为了方便讨论讨论,给以下节点起个别名,

父亲节点:Father

孩子节点:child(父亲节点的孩子节点)

祖父节点:grandfather(父亲节点的父亲节点)

叔叔节点:uncle(如果父亲节点是祖父节点的左孩子,叔叔节点就是祖父节点的右孩子,反之亦然)

如果新节点的父亲节点为黑,插入成功。如果父亲节点为红,需要溯源更新颜色,规则如下:

如果uncle存在且为红色,Father和uncle变为黑色,grandfather变为黑色,让grandfather变为child,继续溯源更新

如果更新至整棵树的根节点(Father为空),让根节点置黑,或uncle为空或uncle黑色,停止溯源更新(uncle为空或uncle黑色后面会讨论)。

如果uncle不存在,或uncle存在且为黑,说明红黑树的平衡被打破了,此时就不需要溯源更新颜色了,需要旋转来恢复红黑树的平衡,旋转之后再更新Father,grandfather或child的颜色

右单旋颜色更新:

Father成为了根需要变成黑色节点,Father旋转之前的孩子节点为黑,该孩子会成为grandfather左孩子,grandfather需要变成红节点。

为什么Father旋转之前的孩子节点为黑呢?因为数据是一个一个插入的,新节点只会和一条路径上的父亲节点冲突,如果这是一颗正常的红黑树,Father旋转之前的孩子节点只能为黑色。Father作为根是黑色的,Father的孩子节点也只能是红色的。下面的旋转也一样

左单旋颜色更新:

还是和右单旋一样,Father成了根需要变成黑色,grandfather需要变成红色。

双旋颜色更新:

无论是左右双旋还是右左双旋,最终都是child变成了根,grandfather和Father变成了child的左右孩子,grandfatjie作为孩子需要变成红色。

代码实现

【C++】详解C++的模板-CSDN博客

enum Color { RED, BLACK };    template <class K>
class RBTreeNode
{
public:typedef RBTreeNode <K> node_type;   K _ky;   node_type* _left;node_type* _right;node_type* _FatherNode;  Color _color;RBTreeNode(const K& key):_ky(key),_left(nullptr),_right(nullptr), _FatherNode(nullptr)  ,_color(RED){}};template <class K> 
class RBTree
{
public:typedef RBTreeNode <K> node_type;RBTree():_root(nullptr)    {}bool Insert(const K& key)//插入元素     {////if (nullptr == _root) //是否是空树{_root = new node_type(key);_root->_color = BLACK;     return true;}//node_type* cur = _root;node_type* fathernode = nullptr;  while (cur){if (cur->_ky < key)    {fathernode = cur;  cur = cur->_right;}else if (cur->_ky > key)     {fathernode = cur;cur = cur->_left;}else{return false;}}cur = new node_type(key); //新插入节点 if (fathernode->_ky < cur->_ky)    {fathernode->_right = cur;cur->_FatherNode = fathernode;}else{fathernode->_left = cur;cur->_FatherNode = fathernode;}while (fathernode && fathernode->_color == RED)  {node_type* grandfather_node = fathernode->_FatherNode;node_type* unclenode = nullptr;if (fathernode == grandfather_node->_left) //父亲节点是祖父节点的左孩子{unclenode = grandfather_node->_right; //叔叔节点是祖父节点的右孩子if (unclenode == nullptr || unclenode->_color == BLACK){if (cur == fathernode->_left)  {RevolveRight(fathernode);fathernode->_color = BLACK;grandfather_node->_color = RED; }else if (cur == fathernode->_right){RevolveLeft(fathernode);RevolveRight(cur);cur->_color = BLACK;   grandfather_node->_color = RED;     }}else{fathernode->_color = BLACK; //父亲变黑unclenode->_color = BLACK;  //叔叔变黑grandfather_node->_color = RED; //爷爷变红cur = grandfather_node;fathernode = cur->_FatherNode; }}else//父亲节点是祖父节点的右孩子{unclenode = grandfather_node->_left; //叔叔节点是祖父节点的左孩子       if (unclenode == nullptr || unclenode->_color == BLACK){if (cur == fathernode->_right){RevolveLeft(fathernode);   fathernode->_color = BLACK;grandfather_node->_color = RED;}else if (cur == fathernode->_left){RevolveRight(fathernode);RevolveLeft(cur);     cur->_color = BLACK;grandfather_node->_color = RED;}}else  {fathernode->_color = BLACK; //父亲变黑unclenode->_color = BLACK;  //叔叔变黑grandfather_node->_color = RED; //爷爷变红cur = grandfather_node;fathernode = cur->_FatherNode;}}}_root->_color = BLACK;  return true;}private:void RevolveLeft(node_type*& fathernode)//左单旋        {node_type* cur = fathernode->_right;  fathernode->_right = cur->_left;if (cur->_left != nullptr)cur->_left->_FatherNode = fathernode;cur->_FatherNode = fathernode->_FatherNode;if (fathernode->_FatherNode != nullptr){if (fathernode->_FatherNode->_right == fathernode){fathernode->_FatherNode->_right = cur;}else{fathernode->_FatherNode->_left = cur;}}cur->_left = fathernode;fathernode->_FatherNode = cur;}void RevolveRight(node_type*& fathernode) //右单旋 {node_type* cur = fathernode->_left;  fathernode->_left = cur->_right;if (cur->_right != nullptr)cur->_right->_FatherNode = fathernode;cur->_FatherNode = fathernode->_FatherNode;if (fathernode->_FatherNode != nullptr){if (fathernode->_FatherNode->_right == fathernode){fathernode->_FatherNode->_right = cur;}else{fathernode->_FatherNode->_left = cur;}}cur->_right = fathernode;fathernode->_FatherNode = cur;}node_type* _root;
};

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

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

相关文章

深度学习实战-yolox训练ExDark数据集(附全过程代码,超详细教程,无坑!)

跳转:数据集获取以及前期准备工作 本人在深度学习实战-yolov5训练ExDark数据集(附全过程代码,超详细教程,无坑!)的数据基础上实现yolox的训练,所以先跳转到该文章下去获取数据集,再继续接下来操作过程。 一、VOC格式数据集制作 1.前期工作 2.转变成voc格式 在datase…

Latex:newcommand

参考文献&#xff1a; latex中自定义的命令———\newcommand-CSDN博客LaTeX技巧924&#xff1a;详解newcommand的参数和默认值 - LaTeX工作室 (latexstudio.net) 文章目录 (re)newcommand自定义的一些命令 (re)newcommand ”定义命令“ 的定义&#xff1a; \newcommand{<…

[每周一更]-(第98期):PHP版本的升级历程

文章目录 大致历程PHP/FI (PHP 1.0)PHP 2.0PHP 3.0PHP 4.0PHP 5.0PHP 5.3 - 5.6PHP 7.0PHP 7.1 - 7.4PHP 8.0PHP 8.1 - 8.2 参考 PHP&#xff0c;即“超文本预处理器”&#xff08;Hypertext Preprocessor&#xff09;&#xff0c;是广泛应用于web开发的服务器端脚本语言。自19…

什么是独特摆动交易策略?fpmarkets1分钟讲清楚

摆动交易策略想必各位投资者都已经接触过了&#xff0c;但是什么是独特摆动交易策略&#xff1f;各位投资者知道吗&#xff1f;其实很简单&#xff0c;这是一种基于斐波纳契工具的独特摆动交易策略。下面fpmarkets1分钟讲清楚&#xff0c;趋势总会经历调整&#xff0c;而这些调…

【机器学习】Python中的决策树算法探索

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 Python中的决策树算法探索引言1. 决策树基础理论1.1 算法概述1.2 构建过程 2. P…

数据集003:猫类识别-12种猫分类数据集 (含数据集下载链接)

数据集简介&#xff1a; 训练集共有2160张猫的图片, 分为12类. train_list.txt是其标注文件 测试集共有240张猫的图片. 不含标注信息. 训练集图像&#xff08;部分&#xff09; 验证集图像&#xff08;部分&#xff09; 标签 部分代码&#xff1a; # 定义训练数据集 class T…

eNSP华为模拟器-DHCP配置

拓扑图 要求 PC1通过DHCP获取192.168.1.1地址PC2和PC3通过DHCP接口地址池方式获取IP地址配置静态路由使其ping通 配置 配置主机名及接口IP地址 # AR1 <Huawei>sys Enter system view, return user view with CtrlZ. [Huawei]sys AR1 [AR1]int g0/0/0 [AR1-Gigabit…

去重复记录和排序——kettle开发09

一、去除重复记录 去除重复记录&#xff0c;就是将数据流中的数据进行字段比较&#xff0c;从而去掉重复值的过程。去除重复记录的前提是需要将数据流中的数据进行排序&#xff0c;然后再进行去重操作。 去除重复记录的逻辑是&#xff0c;如下图&#xff0c;我们将需要比较的…

基础使用-SQL-图形化界面工具DataGrip

一、连接mysql &#xff08;1&#xff09;选择加号&#xff0c;再选择添加一个数据源&#xff08;Data Source&#xff09;&#xff0c;然后选择MySQL &#xff08;2&#xff09;接下来就需要去配置MySQL的连接信息&#xff0c;并且去下载它的驱动&#xff0c;安装驱动时可能要…

微信公众号怎么做留言板功能

在繁忙的都市生活中&#xff0c;你是否常常感到孤单、渴望有一个可以倾诉心声的地方&#xff1f;今天&#xff0c;我要为大家介绍一个特别的角落——我们公众号的留言板功能。它不仅是一个留言板&#xff0c;更是一个情感交流的平台&#xff0c;一个可以让我们彼此心灵相通的桥…

百度发布代码辅助工具,超强

不会用AI的程序员&#xff0c;会跟不会用智能手机的人一样 百度这个代码助手助手感觉还是不错的 https://comate.baidu.com/?inviteCodeijmce7dj 目前看下来这个代码助手是比较强的&#xff0c;比阿里的那个灵码好用&#xff0c;他可以引用到当前的文件&#xff0c;并且能分…

idea改了代码,但是需要紧急切换分支,需要把改动的保存到本地

但是如果有冲突&#xff0c;你没有合并&#xff0c;那也会丢哦&#xff01; 改完那个分支&#xff0c;回到这个分支然后弹出来再。

Delphi 程序例子(DPI变化自动感知及显示器相关功能演示)

目录 一、前言 二、Delphi 演示程序&#xff08;D12版本&#xff0c;用D11也都可以&#xff09; 1. 演示程序功能&#xff1a; 2. 程序界面&#xff1a; 3. 程序源代码下载&#xff08;有偿&#xff09;&#xff1a; 一、前言 系列文章&#xff1a; 彻底搞懂 Windows 显示…

YOLOv5 | 卷积模块 | 提高网络的灵活性和表征能力的动态卷积【附代码+小白可上手】

&#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 轻量级卷积神经网络由于其低计算预算限制了CNNs的深度&#xff08;卷积层数&#xff09;和宽度&#xff08;通道数&#xff09;&#xff0c;…

三分钟一条AI小和尚视频 ,日引300+创业粉。单日变现四位数 全套工具

经过六个月的不懈努力和无数次的尝试错误&#xff0c;我终于找到了一个高效引流和积累粉丝的新策略&#xff0c;并愿意与大家无私分享。这一次&#xff0c;我将详尽地介绍这个方法&#xff0c;建议朋友们多次观看以彻底掌握其精髓。 简而言之&#xff0c;该策略主要依托于AI绘…

C语言文件编程

C语言文件编程 第一部分 基本概念 1、Linux文件类型 1.-普通文件&#xff1a;存在于外部存储器中&#xff0c;用于存储普通数据。 1.txt 1.c 1.mp3 1.mp4 2.d目录文件&#xff1a;用于存放目录项&#xff0c;是文件系统管理的重要文件类型。 文件夹 3.p管道文件&#x…

基于springboot+vue的“漫画之家”系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

全新交友盲盒+付费进群二合一源码 包含全套源码+教程

盲盒交友脱单系统源码&#xff0c;带教程&#xff0c;免授权这套源码已经替你们搭建测试过了 附带进群系统&#xff0c;定位是正常的 申明需要无限回调&#xff0c;没有回调的搭建出来不能用不要说源码不能用 全新系统方便大家使用&#xff0c;已经录制好详细的教程&#xf…

【Linux】-Spark分布式内存计算集群部署[20]

注意&#xff1a; 本节的操作&#xff0c;需要前置准备好Hadoop生态集群&#xff0c;请先部署好Hadoop环境 简介 Spark是一款分布式内存计算引擎&#xff0c;可以支持海量数据的分布式计算。 Spark在大数据体系是明星产品&#xff0c;作为最新一代的综合计算引擎&#xff0c…

闲话 .NET(6):.NET Core 各个版本的特性

前言 之前我们聊了一下 .NET Core 有哪些优势&#xff0c;.NET Core 发展非常迅速&#xff0c;不过短短几年&#xff0c;.NET Core 已经发布 .NET 8 了&#xff0c;基本上保持了一年一个版本的速度&#xff0c;每个版本都有自己的独有特性&#xff0c;下面我们来简单的盘点一下…