【数据结构】图解红黑树以及代码实现

目录

一、相关概念

性质

二、图解

1、插入操作

2、parent在左边情况1:cur为红色节点parent也是红色节点、uncle也为红色节点

3、parent在左边情况2:cur为红色节点parent也是红色节点、uncle为黑色或者是空,cur是parent的left

4、parent在左边情况3:cur为红色节点parent也是红色节点、uncle为黑色或者是空,cur是parent的right

5、parent在右边情况1:cur为红色节点parent也是红色节点、uncle也为红色节点

6、parent在右边情况2:cur为红色节点parent为红色节点、uncle为黑色或者是空,cur是parent的right

7、parent在右边情况3:cur为红色节点parent为红色节点、uncle为黑色或者是空,cur是parent的left

三、代码实现

1、准备节点

2、实现插入

3、 代码实现


一、相关概念

红黑树是一种自平衡的二叉查找树

红黑树是计算机科学中广泛使用的数据结构,通常用于实现关联数组等数据结构,以保证在最坏情况下仍具有较好的查找性能。下面是其特点和性质:

  1. 节点颜色:每个节点都有一个颜色属性,可以是红色或黑色。
  2. 根节点为黑:红黑树的根节点总是黑色的。
  3. 叶节点为黑:所有叶子节点(NIL)都是黑色的。这里的叶子节点指的是没有子节点的空链接。
  4. 路径长度限制:从根节点到任何叶子节点的简单路径上,均不会出现两个连续的红色节点,这意味着没有任何一个路径会比其它路径长出两倍。
  5. 查找效率:红黑树可以在 O(log N) 时间内完成查找、增加、删除等操作,其中 N 是树中元素的数量。

此外,红黑树的本质是对概念模型2-3-4树的一种实现。2-3-4树是阶数为4的B树,也是平衡多路查找树的一种,它通过保持树的平衡来确保即使在最坏情况下也能保持 O(Log N) 的时间复杂度进行查找操作。

性质
  1. 最长路径是最短路径的2倍
  2. 根节点是黑色的
  3. 如果一个节点是红色的,那么他的两个孩子节点是黑色的
  4. 对于每个节点,该节点到其所有后代的叶子节点的简单路径上均包含相同数目的黑色节点

二、图解

1、插入操作

每次插入节点的颜色都为红色,按照二叉搜索树的方式插入后,开始从插入的节点向上调整,使得这棵树满足红黑树上述的性质,在进行调整的过程中大致左右树类似分为3中情况,在调整之前的插入部分图解如下:

2、parent在左边情况1:cur为红色节点parent也是红色节点、uncle也为红色节点

3、parent在左边情况2:cur为红色节点parent也是红色节点、uncle为黑色或者是空,cur是parent的left

这种情况不可能是刚插入cur节点而引起的,因为此时grandparent是黑色的,此时parent路径上比uncle路径上的黑色节点少一个,所以这种情况是发生在调整的过程中的。

4、parent在左边情况3:cur为红色节点parent也是红色节点、uncle为黑色或者是空,cur是parent的right

这种情况也是发生在向上调整的过程中的

5、parent在右边情况1:cur为红色节点parent也是红色节点、uncle也为红色节点

与parent是grandparent的左边类型,右边的操作全部镜像

6、parent在右边情况2:cur为红色节点parent为红色节点、uncle为黑色或者是空,cur是parent的right

与上述parent是grandparent左边的情况2镜像相同,同样发生在调整的 过程中

7、parent在右边情况3:cur为红色节点parent为红色节点、uncle为黑色或者是空,cur是parent的left

与上述情况类型,将情况3转变为情况2去处理

三、代码实现

1、准备节点

首先一颗红黑树的节点需要哪些字段呢?需要数据域,以及左孩子指针右孩子指针还需存储他的父亲节点的指针,其次就是这个节点颜色

1.字段

2、实现插入

首先红黑树的插入主要分为两步将节点按照红色进行插入,插入方法与二叉搜索树插入相同。插入完成后按照其性质进行调整(左右旋转图示详解在AVL树)

插入

调整

插入节点颜色是红色,此时如果他的父亲节点也是红色,那么就不满足红黑树的性质,此时就需要去进行分情况调整,主要情况在二中详细图示

3、 代码实现
#include<iostream>
using namespace std;

/**
 * 红黑树的颜色
 */
enum COLOR {
RED,
BLACK
};

/**
 * 红黑树节点
 */
typedef struct RBTNode {
int val;
RBTNode* left;
RBTNode* right;
RBTNode* parent;
COLOR color;
}RBTNode, *TreeNode;

/**
 * 创建并初始化一个根节点
 * @param val
 * @return
 */
TreeNode NewRBTreeNode (int val) {
    auto node = (TreeNode) malloc(sizeof(RBTNode));
    node->val = val;
    node->color = RED;
    node->left = node->right = node->parent = nullptr;
    return node;
}

/**
 * 右旋
 * @param root 根节点
 * @param parent 旋转节点
 */
void rotateRight(TreeNode& root, TreeNode& parent) {
    TreeNode subL = parent->left;
    TreeNode subLR = subL->right;
    TreeNode pParent = parent->parent;

    parent->left = subLR;
    subL->right = parent;
    if (subLR != nullptr) subLR->parent = parent;
    parent->parent = subL;

    if (parent == root) {
        root = subL;
        subL->parent = nullptr;
    } else if (parent == pParent->left) {
        pParent->left = subL;
        subL->parent = pParent;
    } else {
        pParent->right = subL;
        subL->parent = pParent;
    }
}

/**
 * 左旋
 * @param root 根节点
 * @param parent 旋转节点
 */
void rotateLeft(TreeNode& root, TreeNode& parent) {
    TreeNode subR = parent->right;
    TreeNode subRL = subR->left;
    TreeNode pParent = parent->parent;

    parent->right = subRL;
    subR->left = parent;
    if (subRL != nullptr) subRL->parent = parent;
    parent->parent = subR;

    if (parent == root) {
        root = subR;
        subR->parent = nullptr;
    } else if (parent == pParent->left) {
        pParent->left = subR;
        subR->parent = pParent;
    } else {
        pParent->right = subR;
        subR->parent = pParent;
    }
}

/**
 * 插入
 * @param root 根节点
 * @param val  要插入的元素
 * @return
 */
bool Insert(TreeNode& root,int val) {
    if (root == nullptr) {
        root = NewRBTreeNode(val);
        root->color = BLACK;
        return true;
    }

    TreeNode cur = root, parent = nullptr;
    while (cur) {
        if (cur->val > val) {
            parent = cur;
            cur = cur->left;
        } else if (cur->val < val) {
            parent = cur;
            cur = cur->right;
        } else {
            return false;
        }
    }

    TreeNode node = NewRBTreeNode(val);
    if (parent->val > val) parent->left = node;
    else parent->right = node;
    node->parent = parent;

    cur = node;
    // 调整的前提是插入节点是红色的且他的父亲也是红色 或者 在调整过程中使得cur是红色的parent也是红色的
    while (parent != nullptr && parent->color == RED) {
        TreeNode grandParent = parent->parent;  // 只要parent不为空且为红色,这个节点就不可能为空,因为根节点颜色是黑色的
        if (parent == grandParent->left) {
            TreeNode uncle = grandParent->right;
            if (uncle != nullptr && uncle->color == RED) {
                // case1:  red(cur parent uncle)
                grandParent->color = RED;
                uncle->color = parent->color = BLACK;

                cur = grandParent;
                parent = cur->parent;
            } else {
                if (cur == parent->right) {
                    // case3: red(cur parent) black or null(uncle)  cur == parent->right
                    rotateLeft(root, parent);
                    TreeNode tmp = parent;
                    parent = cur;
                    cur = tmp;
                }  // case3 -> case2

                // case2: red(cur parent) black or null(uncle) cur == parent.left
                rotateRight(root, grandParent);
                grandParent->color = RED;
                parent->color = BLACK;
            }
        } else {
            TreeNode uncle = grandParent->left;
            if (uncle != nullptr && uncle->color == RED) {
                // case1: red(cur parent uncle)
                parent->color = uncle->color = BLACK;
                grandParent->color = RED;

                cur = grandParent;
                parent = cur->parent;
            } else {
                if (cur == parent->left) {
                    // case3: red(cur parent) black or null(uncle)  cur == parent->left
                    rotateRight(root, parent);
                    TreeNode tmp = cur;
                    cur = parent;
                    parent = tmp;
                }

                // case2: red(cur parent) black or null(uncle) cur == parent.right
                rotateLeft(root, grandParent);
                grandParent->color = RED;
                parent->color = BLACK;
            }
        }
    }
    root->color = BLACK;
    return true;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【清灰教程】联想拯救者Y7000p(2018款)拆机清灰教程+更换硅脂

清灰教程 本人电脑&#xff1a;联想拯救者Y7000p&#xff08;2018款&#xff09;第一步&#xff1a;购买清灰道具&#xff08;提前买好&#xff09;螺丝刀1.硅脂 这里随便买的 2.刮刀&#xff08;买硅脂送&#xff09;4.刷子&#xff08;清风扇灰&#xff09;5.撬后盖用&#x…

wordpress主题给网站增加一个版权声明区块代码分享

在数字化时代&#xff0c;网络上的信息传播变得越来越便捷&#xff0c;给人们生活和工作带来了极大的便利。然而&#xff0c;在这个过程中也产生了很多版权问题。为了更好地保护自己的版权&#xff0c;许多网站开始在其网页上添加版权声明。本文将探讨在网站上添加版权声明的重…

sklearn线性回归--岭回归

sklearn线性回归--岭回归 岭回归也是一种用于回归的线性模型&#xff0c;因此它的预测公式与普通最小二乘法相同。但在岭回归中&#xff0c;对系数&#xff08;w&#xff09;的选择不仅要在训练数据上得到好的预测结果&#xff0c;而且还要拟合附加约束&#xff0c;使系数尽量小…

rust语言初识

程序设计实践课上水一篇ing 来源&#xff1a;rust基础入门-1.初识rust-酷程网 (kucoding.com) rust作为一名新兴语言&#xff0c;与go又有些许不同&#xff0c;因为它的目标是对标系统级开发&#xff0c;也就是C、C这两位在编程界的位置。比如我们最常用的windows系统&#x…

韩语“再见” 怎么说,柯桥韩语培训

1.1 标准写法及读法 안녕 (annyeong) 音译&#xff1a; 安宁 罗马音&#xff1a; Annyeong 使用情境&#xff1a; 适用于朋友之间或非常熟悉的关系中&#xff0c;不分场合&#xff0c;可以用于打招呼或告别&#xff0c;表示“你好”或“再见”。 안녕히 가세요 (annyeonghi …

Elasticsearch之文本分析

文本分析基本概念 官网&#xff1a;Text analysis | Elasticsearch Guide [7.17] | Elastic 官网称为文本分析&#xff0c;这是对文本进行一直分析处理的方式&#xff0c;基本处理逻辑是为按照预先制定的分词规则&#xff0c;把原本的文档进行分割成多个小颗粒度的词项&#x…

只争朝夕,不负韶华!

学生成绩分析云平台是基于云计算技术和数据分析算法的在线平台&#xff0c;用于对学生的学业成绩进行全面的分析和评估。学生成绩分析云平台可以为学校、教育机构和教师提供全面的学生成绩管理和分析解决方案&#xff0c;帮助他们更好地了解学生的学业表现、优化教学策略&#…

HC32F103BCB使用SPI获取AS5040编码器数据

1.AS5040介绍 2.硬件电路 硬件上使用SSI通信方式连接。 3.配置硬件SPI 查看手册&#xff0c;AS5040时序 可以看到在空闲阶段不发生数据传输的时候时钟(CLK)和数据(DO)都保持高电位(tCLKFE阶段)&#xff0c;在第一个脉冲的下降沿触发编码器载入发送数据&#xff0c;然后每一个…

算法设计第七周(应用哈夫曼算法解决文件归并问题)

一、【实验目的】 &#xff08;1&#xff09;进一步理解贪心法的设计思想 &#xff08;2&#xff09;掌握哈夫曼算法的具体应用 &#xff08;3&#xff09;比较不同的文件归并策略&#xff0c;探讨最优算法。 二、【实验内容】 设S{f1,…,fn}是一组不同的长度的有序文件构…

【测评】OrangePi AIPro环境配置与基础应用

1.介绍 官网&#xff1a;http://www.orangepi.cn/ 社区&#xff1a;http://forum.orangepi.cn/ 昇腾社区&#xff1a;https://www.hiascend.com/ OrangePi AIPro 是一款基于昇腾AI技术的开发板&#xff0c;它采用华为昇腾910E AI芯片&#xff0c;集成4核64位CPU和AI处理器&am…

CRMEB多门店的门店后台首页路由

如何在输入 http://localhost:8080/、http://localhost:8080/store/、http://localhost:8080/custom-store/ 这三个中任意一个链接都能正确跳转到 http://localhost:8080/store/home/index 。要实这个要求&#xff0c;有两种方式&#xff1a; 重定向const router new VueRout…

恒创科技:Linux 服务器和 Windows 服务器哪个更好?

选择正确的服务器系统至关重要&#xff0c;目前广泛使用的选项是 Windows 服务器 和 Linux 服务器&#xff0c;它们各有优缺点。本文将比较 Linux 与 Windows 服务器&#xff0c;让我们来看看它们的主要区别&#xff0c;然后再决定哪种操作系统适合使用。 主要区别&#xff1a;…

几种流行的并行方法了解

几种流行的并行方法&#xff1a; 数据并行&#xff08;data parallel&#xff09;模型并行&#xff08;model parallel&#xff09; tensor并行pipeline并行sequence并行Zero Redundancy Data Parallelism&#xff08;ZeRO&#xff09; Data parallelism (DP) 经典的数据并行…

基本Java语法和语义 (Reading 2)

&#xff08;1&#xff09;Java和C在变量类型命名和使用 基本数据类型 对象类型与引用类型 特殊类型 关键字和修饰符 &#xff08;2&#xff09;快照图&#xff1a; IDE调试工具: 许多IDE&#xff08;如Eclipse、IntelliJ IDEA&#xff09;提供了调试功能&#xff0c;可以…

智慧水坝:科技变革的里程碑

在曾经的水利工程领域&#xff0c;水坝只是为了水资源的调配和控制&#xff0c;提供一定的安全储备。然而&#xff0c;随着现代科技的不断发展&#xff0c;传统的水坝已经不再是单一的水源控制工程&#xff0c;而是变成了一个充满智慧与创新的生态系统。智慧水坝的概念已经超越…

远大阀门集团携创新产品亮相南京,展现石化行业新风采

2024年5月22日&#xff0c;备受瞩目的第八届中国石油和化工行业采购大会在江苏省南京市盛大开幕。作为石化行业物资采购领域极具影响力的年度盛会&#xff0c;本次大会吸引了众多国内外能源化工企业、化工新材料企业、工程公司以及相关领域的供应商参加。远大阀门集团作为特邀优…

Python筑基之旅专栏(导航)

目录 一、Python筑基之旅专栏博文清单及链接 二、推荐阅读 一、Python筑基之旅专栏博文清单及链接 01、溯源及发展 02、变量和数据类型 03、搭建Python开发环境及库 04、两个重要函数/列表/元组 05、字符串(一) 06、字符串(二) 07、字符串(三) 08、字典 09、集合 10…

看汽车冲压件的工厂,如何做PFMEA分析?

为了确保冲压件的质量稳定&#xff0c;提高生产效率&#xff0c;PFMEA&#xff08;过程潜在失效模式及影响分析&#xff09;分析成为了汽车冲压件工厂不可或缺的重要工具。本文将带您走进汽车冲压件工厂&#xff0c;一探PFMEA分析的奥秘与实践。 PFMEA分析&#xff0c;作为一种…

I.MX6ULL的蜂鸣器实验

系列文章目录 I.MX6ULL的蜂鸣器实验 I.MX6ULL的蜂鸣器实验 系列文章目录一、前言二、有源蜂鸣器简介三、硬件原理分析四、程序编写五、编译下载验证5.1编写 Makefile 和链接脚本5.2编译下载 一、前言 在 I.MX6U-ALPHA 开发板上有一个有源蜂鸣器&#xff0c;通过 IO 输出高低电…

git中忽略文件的配置

git中忽略文件的配置 一、在项目根目录下创建.gitignore文件二、配置规则如果在配置之前已经提交过文件了&#xff0c;要删除提交过的&#xff0c;如何修改&#xff0c;参考下面的 一、在项目根目录下创建.gitignore文件 .DS_Store node_modules/ /dist# local env files .env…