【数据结构】二叉树---红黑树的实现

目录

一.  红黑树的概念及性质

二.  红黑树结点结构的定义

三.  红黑树的插入操作

     1. 情况一

     2. 情况二

       3. 情况三

四.  红黑树的验证

五.  红黑树与AVL树的比较


一.  红黑树的概念及性质

红黑树是一种自平衡的二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,
可以是红色或黑色。

红黑树具有以下性质

   1. 每个结点要么是红色,要么是黑色
   2. 根结点是黑色
   3. 每个叶结点(NIL结点,空结点)是黑色
   4. 如果一个结点红色,则它的子结点必须黑色(所有路径上不能有两个连续的红色节点)

   5. 从任一结点到其每个叶子的所有路径都包含相同数目黑色


        这些性质保证了红黑树的关键性质,从根到叶子的最长路径不会超过最短路径的两倍,从而保证了红黑树的平衡性。红黑树常被用于实现集合和映射等数据结构,因为其在插入、删除等操作上具有较好的性能表现

二.  红黑树结点结构的定义

红黑树节点结构通常包含以下几个字段:

       键值(key):用于比较和排序节点的值。
       指向父节点的指针(parent):指向当前节点的父节点。
       指向左节点的指针(left):指向当前节点的左节点。
       指向右节点的指针(right):指向当前节点的右节点。
       颜色(color):表示节点的颜色,通常用一个位来表示,比如红色和黑色

红黑树节点结构的定义表示如下:

在这里通过定义枚举结构来表示红、黑两种颜色

//定义结点的颜色
enum Color
{RED,    //表示红色BLACK   //表示黑色
};//红黑树的结点定义类模板
template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;    //指向左结点RBTreeNode<T>* _right;   //指向右节点RBTreeNode<T>* _parent;  //指向父节点(红黑树需要旋转,为了实现简单给出该字段)          T _data;        //结点值Color _col;     //结点的颜色//结点的构造函数RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED)      //默认初始化红色{}};

注意:这里将插入结点的默认颜色给成红色

  

       当插入一个新节点时,如果将该节点默认设置为黑色,可能会破坏红黑树的性质,需要进行额外的操作来恢复平衡。而将新插入的节点默认设置为红色,是为了满足红黑树的性质,特别是在插入节点时能够更容易地维持这些性质。红黑树的性质要求如果一个节点是红色,则其子节点必须是黑色,这是为了确保从根节点到叶子节点的每条路径上黑色节点的数量相等,从而保持树的平衡性。

       因此,将节点的默认颜色设置为红色是为了简化插入操作,并确保在插入新节点后能够更轻松地维护红黑树的平衡性。

三.  红黑树的插入操作

       因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整但当新插入节点的双亲节点颜色为红色时,就违反了性质4,不能有连续的红色节点,此时需要对红黑树分情况来讨论:

说明:这里所用到的字段
                C 表示当前新插入节点,

                P 表示父节点(当前节点的父节点),
                G 表示祖父节点(父节点的父节点),

                U 表示叔叔节点(父节点的兄弟节点)

     1. 情况一

新插入节点(C)的父节点(P)是红色,且新插入节点的叔叔节点(U)(父节点的兄弟节点) 存在且是红色
这时需要进行颜色调整和递归调整。     

颜色调整:将 P 和 U 改为 黑,G 改为 红

注意:G若为根结点,则改为黑色

           G若为子树,则继续递归向上调整

     2. 情况二

新插入节点(C)的父节点(P)是红色,但新插入结点的叔叔节点(U)是黑色或空节点:

 叔叔结点(U)不为空

    1. 父节点(P)是其祖父节点(G)的左节点,新插入节点(C)是其父节点(P)的右节点,左单旋转

    2. 父节点(P)是其祖父节点(G)的右节点,新插入节点(C)是其父节点(P)的左节点,右单旋转


 颜色调整 :G 改为 红,C 改为 黑            

       3. 情况三

新节点(C)的父节点(P)是红色,但新节点的叔叔节点(U)是黑色或空节点

 叔叔结点(U)为空

    1. 父节点(P)是其祖父节点(G)的左节点,新插入节点(C)是其父节点(P)的左节点,单旋转

    2. 父节点(P)是其祖父节点(G)的右节点,新插入节点(C)是其父节点(P)的右节点,单旋转

颜色调整 :P 改为 黑,G 改为 红

	bool Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_data> data){parent = cur;cur = cur->_left;}else if (cur->_data < data){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(data); if (cur->_data < parent->_data){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;//一:父节点在祖父节点的左侧if (parent == grandfather->_left){Node* uncle = grandfather->_right;//情况1:叔叔节点存在且为红;if (uncle && uncle->_col == RED){//父和叔叔节点变为黑色,祖父节点变为红色parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//继续往上处理cur = grandfather;parent = grandfather->_parent;}else{//情况2:叔叔不存在 或 叔叔存在且为黑色//旋转 + 变色if (cur == parent->_left) //cur在parent的左{RotateR(grandfather);   //以grandfather进行右单旋转parent->_col = BLACK;   //调整颜色grandfather->_col = RED;}else                      //cur在parent的右{RotateL(parent);         //以parent进行左旋RotateR(grandfather);    //以grandfather进行右旋cur->_col = BLACK;       //调整颜色grandfather->_col = RED; }break;}}else  //二:父节点在祖父节点的右侧{Node* uncle = grandfather->_left;//情况1:叔叔节点存在且为红;if (uncle && uncle->_col == RED){//父和叔叔节点变为黑色,祖父节点变为红色parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//继续往上处理cur = grandfather;parent = grandfather->_parent;}else{//情况2:叔叔不存在 或 叔叔存在且为黑色//旋转 + 调整颜色// cur在parent的右if (cur == parent->_right){RotateL(grandfather);   //以grandfather进行右单旋转parent->_col = BLACK;   //调整颜色grandfather->_col = RED;}else  // cur在parent的左{RotateR(parent);         //以parent进行右旋RotateL(grandfather);    //以grandfather进行左旋cur->_col = BLACK;       //调整颜色grandfather->_col = RED;}break;}}}_root->_col = BLACK;                  return true;}// 左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)                  subRL->_parent = parent;subR->_left = parent;Node* ppnode = parent->_parent;         parent->_parent = subR;                 if (parent == _root)                    {_root = subR;subR->_parent = nullptr;}else{if (ppnode->_right == parent){ppnode->_right = subR;}else{ppnode->_left = subR;}subR->_parent = ppnode;}}// 右单旋void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)                  subLR->_parent = parent;subL->_right = parent;Node* ppnode = parent->_parent;     parent->_parent = subL;             if (parent == _root)                {_root = subL;subL->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}}

四.  红黑树的验证

红黑树的验证分为两步:

     1. 验证其是否满足二叉搜索树

                中序遍历是否为有序序列

     2. 验证其是否满足红黑树的性质

              1. 根是黑色的
              2. 没有连续的红色节点
              3. 每条路径的黑色节点的数量相等

	//中序遍历 验证是否有序void InOrder(Node* root){if (root == nullptr)return;InOrder(root->_left);cout << root->_data << " ";InOrder(root->_right);}//验证是否满足红黑树的性质bool IsValidRBTRee(){if (_root && _root->_col == RED)return false;int refBlackNum = 0;  //作为路径上黑色节点的参考值Node* cur = _root;while (cur){if (cur->_col == BLACK)refBlackNum++;cur = cur->_left;}return _IsValidRBTRee(_root, 0, refBlackNum);}// 检测红黑树是否为有效的红黑树bool _IsValidRBTRee(Node* root, int blackNum, int refBlackNum){if (root == nullptr)  //每条路径的黑色节点的数量是否相等{if (blackNum != refBlackNum){cout << "黑色节点的数量不相等" << endl;return false;}cout << blackNum << endl;   return true;}//是否存在连续的红节点if (root->_col == RED && root->_parent->_col == RED){cout << root->_kv.first << "存在连续的红色节点" << endl;return false;}// 统计黑色节点的个数if (root->_col == BLACK){blackNum++;}return _IsValidRBTRee(root->_left, blackNum, refBlackNum)&& _IsValidRBTRee(root->_right, blackNum, refBlackNum);}

五.  红黑树与AVL树的比较

红黑树和AVL树是两种常见的自平衡二叉搜索树结构。它们都可以保持树的平衡,以确保在插入和删除操作后,树的高度始终保持在较小的范围内,从而保证了检索、插入和删除操作的时间复杂度为O(logn)

 一,对平衡性要求  

        1. AVL树要求严格的平衡性,即任意节点的左右子树的高度差的绝对值不超过1

        2. 红黑树放宽了对平衡性的要求,它通过引入红黑节点的颜色来保持大致平衡,即任意节点的黑结点高度相等

二,插入和删除操作的性能

        1. AVL树对插入和删除操作的平均性能略低于红黑树,因为AVL树需要更频繁地进行旋转操作来保持严格的平衡。

        2. 红黑树在插入和删除操作时的性能相对较好,因为它对平衡性的要求较松,旋转的次数相对较少

三,查询性能

        由于两种树结构的高度始终保持在较小范围内,它们的查询性能相似,都为O(logn)


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

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

相关文章

并发编程之创建线程的几种方式以及运行的详细解析

3.1 创建和运行线程 方法一&#xff0c;直接使用 Thread // 创建线程对象 Thread t new Thread() {public void run() {// 要执行的任务} }; // 启动线程 t.start(); 例如&#xff1a; // 构造方法的参数是给线程指定名字&#xff0c;推荐 Thread t1 new Thread("t1…

mysql中用逗号隔开的某字段,如何判断其他表的字段值是否在这个字段中

因为要增加需求&#xff0c;需要将线上表中老数据&#xff0c;修改为新数据的规则。 线上两张表&#xff0c;sequence_number中is_use有3作废、2到期状态&#xff0c;需要根据这个状态和school_ai_authorization中的is_deleted修改新增的state字段。 sequence_number表结构&…

Mint_21.3 drawing-area和goocanvas的FB笔记(八)

FreeBASIC与TinyPTC绘图 TinyPTC是个操作非常简单的库&#xff0c;有各平台用的版本&#xff0c;linux平台版本又分为sdl版和gfx版&#xff0c;gfx版本比sdl版本速度快&#xff0c;尺寸不大&#xff0c;可操作的函数只有3个&#xff1a; ptc_open, ptc_update, ptc_close …

若依框架的使用

文章目录 0,项目介绍项目架构项目原型 1,前端2,后端3,数据库4,测试5,二开5.1 创建新module5.2 添加pom依赖5.3 添加测试代码5.4 测试5.5 跳过验证(后期要改回来)5.6 修改前端路由5.7 添加前端菜单5.7.1 一级菜单5.7.2 二级菜单5.7.3 测试 5.8 课程列表-后台5.8.0 项目结构5.8.1…

VisionPro安装教程

安装 前言: 私信获取软件安装包 一、 关闭电脑管家 关闭防火墙 二、 解压此文件 三、 解压完成 找到DISK1目录 在setup.exe文件 右键 以管理员身份运行 四、 等待 五、 点击 “下一步 六、 点击 下一步 七、 接受条款 点击下一步 八、 填写邮箱 九、 点击下一…

案例分析篇01:软件架构设计考点架构风格及质量属性(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章推荐: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html 【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-…

pytorch(十)循环神经网络

文章目录 卷积神经网络与循环神经网络的区别RNN cell结构构造RNN例子 seq2seq 卷积神经网络与循环神经网络的区别 卷积神经网络&#xff1a;在卷积神经网络中&#xff0c;全连接层的参数占比是最多的。 卷积神经网络主要用语处理图像、语音等空间数据&#xff0c;它的特点是局部…

复现文件上传漏洞

一、搭建upload-labs环境 将下载好的upload-labs的压缩包&#xff0c;将此压缩包解压到WWW中&#xff0c;并将名称修改为upload&#xff0c;同时也要在upload文件中建立一个upload的文件。 然后在浏览器网址栏输入&#xff1a;127.0.0.1/upload进入靶场。 第一关 选择上传文件…

Qt/QML编程之路:基于QWidget编程及各种2D/3D/PIC绘制的示例(45)

关于使用GWidget,这里有一个示例,看了之后很多图形绘制,控件使用,及最基本的QWidget编程都比较清楚了。ui的绘制: 运行后的界面如 工程中有非常丰富的关于各种图形的绘制,比如上图中circle,还有image。有下面一段readme的说明: # EasyQPainter Various operation pra…

AI预测福彩3D第9弹【2024年3月15日预测--第2套算法重新开始计算第1次测试】

我有一个彩友交流群&#xff0c;里面有很多热衷研究彩票技术的彩友&#xff0c;经过群里很多彩友的建议&#xff0c;我昨天晚上又设计了一套新的算法&#xff0c;这套算法加入了012路权重&#xff0c;将012路的权重按照开出概率设为不同的权重系数。 然后与第1套算法的进行相乘…

RabbitMq踩坑记录

1、连接报错&#xff1a;Broker not available; cannot force queue declarations during start: java.io.IOException 2.1、原因&#xff1a;端口不对 2.2、解决方案&#xff1a; 检查你的连接配置&#xff0c;很可能是你的yml里面的端口配置的是15672&#xff0c;更改为5672即…

STM32基础--中断应用

本文章里面假设中断就是异常&#xff0c;不做区分。 异常类型 F103 在内核水平上搭载了一个异常响应系统&#xff0c;支持为数众多的系统异常和外部中断。其中系统异常有 8 个&#xff08;如果把 Reset 和 HardFault 也算上的话就是 10 个&#xff09;&#xff0c;外部中断有…