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

数据结构之——红黑树

一、红黑树简介

        红黑树是一种自平衡二叉查找树,在计算机科学中有广泛应用。它是在 1972 年由 Rudolf Bayer 发明的,当时被称为平衡二叉 B 树,在 1978 年被 Leo J. Guibas 和 Robert Sedgewick 改称为 “红黑树”。

红黑树的特点如下:

  • 每个节点非红即黑。
  • 根节点总是黑色的。
  • 每个叶子节点都是黑色的空节点(NIL 节点)。
  • 如果节点是红色的,则它的子节点必须是黑色的(反之不一定),通常这条规则也叫不会有连续的红色节点。
  • 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。

        在 Java 集合框架中,很多部分(HashMap,TreeMap,TreeSet 等)都有红黑树的应用。在 C++ STL 中,很多部分 (包括 set, multiset,map, multimap) 应用了红黑树的变体。其他平衡树还有:AVL,SBT,伸展树,TREAP 等等。

        红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:

  • 性质 1. 节点是红色或黑色。
  • 性质 2. 根节点是黑色。
  • 性质 3. 每个叶节点(NIL 节点,空节点)是黑色的。
  • 性质 4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
  • 性质 5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

        这些约束强制了红黑树的关键性质:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。

二、红黑树的特点

红黑树具有以下五个重要特点:

1.每个节点非红即黑:

  • 红黑树中的节点只能是红色或者黑色,不存在其他颜色状态。这一特性明确了节点的颜色属性,为后续的规则和操作提供了基础。

2.根节点总是黑色:

  • 根节点在红黑树中具有特殊地位,它必须是黑色的。这一规定有助于保持树的整体平衡性,避免出现极端情况。

3.红色节点的父节点和子节点不能为红色:

  • 如果一个节点是红色的,那么它的父节点和子节点必须是黑色的。这一规则确保了从每个叶子到根的所有路径上不能有两个连续的红色节点,从而维持了树的平衡性和稳定性。

4.所有的叶子节点都是黑色的空节点:

  • 红黑树中的叶子节点都是黑色的空节点,也被称为 NIL 节点。这些空节点在树的结构中起到了重要的作用,它们帮助确定了树的边界和路径的长度。

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

  • 这一特点是红黑树保持平衡的关键之一。无论从哪个节点开始,到其每个叶子节点的所有路径上,黑色节点的数目都是相同的。这意味着树的不同分支在黑色节点的数量上保持了一致性,从而保证了树的大致平衡。

        红黑树的这些特点使得它在计算机科学中有广泛的应用。例如,在 Java 集合框架中的 HashMap、TreeMap、TreeSet 等数据结构中,以及 C++ STL 中的部分数据结构中,都应用了红黑树的变体。与其他平衡树如 AVL、SBT、伸展树、TREAP 等相比,红黑树在实现上更加灵活,并且在插入和删除节点时的效率更高。它能够在保证高效查找、插入和删除操作的同时,保持树的大致平衡,从而提高了数据结构的性能和稳定性。

三、红黑树与其他数据结构的对比

1. 与二叉树对比

        二叉树没有自平衡机制,可能导致树的不平衡。例如,当大量插入有序数据时,二叉树可能会变得线性化,结构不平衡。而红黑树通过节点的颜色标记和旋转操作来保持平衡。红黑树中的每个节点非红即黑,根节点总是黑色的,红色节点的两个子节点必须是黑色的,从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。这些规则确保了红黑树在插入和删除节点时能够保持相对平衡,避免出现极端不平衡的情况。

2. 与 AVL 树对比

        AVL 树对平衡性要求更高,在任何节点的左右子树高度差不超过 1。这使得 AVL 树在插入和删除操作时的性能开销较大,因为需要频繁地进行旋转操作来维持平衡。而红黑树更常用于各种常规应用,在插入和删除操作中具有较好的平均和最坏情况时间复杂度。红黑树是一种弱平衡二叉树,它不要求像 AVL 树那样严格的平衡条件,但通过颜色标记和旋转操作,也能保证树的大致平衡,同时减少了旋转操作的次数,提高了性能。

3. 与 B 树和 B + 树对比

        B 树和 B + 树适用于处理大规模数据和磁盘存储的情况。B 树是一种多路搜索树,每个节点可以包含多个键值对,通过分裂和合并节点来维持平衡。B + 树在 B 树的基础上进行了改进,非叶子节点只存储索引关键字数据,叶子节点数据之间通过双向链表链接,方便范围检索。而红黑树适用于内存中的数据结构。红黑树是一种二叉查找树,通过颜色标记和旋转操作来保持平衡,适用于内存中数据的快速查找、插入和删除操作。它的高度相对较低,能够在 O (logN) 的时间复杂度内完成这些操作。

四、红黑树的实现原理

1.红黑树的概念

        红黑树是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是 Red 或 Black。

2.红黑树的性质

  • 每个节点不是红色就是黑色。
  • 根结点是黑色的。
  • 如果一个结点是红色的,则它的两个孩子是黑色的。
  • 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
  • 每个叶子结点的都是黑色的(此处叶子结点指的是空节点)。

3.红黑树结点的定义

        红黑树结点的结构体通常包括左右孩子指针、父指针、值域和颜色。例如,在一些实现中,可能定义如下:

#include <stdio.h>
#include <stdlib.h>

// 定义节点颜色的枚举类型
typedef enum {
    RED = 0,
    BLACK = 1
} ColorType;

// 定义键值的类型为整数,这里可根据实际需求修改为其他合适的类型
typedef int KeyType;

// 定义红黑树节点结构体
typedef struct rb_node {
    // 指向左子节点的指针
    struct rb_node* leftchild;
    // 指向父节点的指针
    struct rb_node* parent;
    // 指向右子节点的指针
    struct rb_node* rightchild;
    // 节点的颜色,取值为RED或BLACK
    ColorType color;
    // 节点的键值,用于比较和排序节点在树中的位置
    KeyType key;
} rb_node;

// 以下可以添加对红黑树的各种操作函数,例如插入、删除、查找、遍历等函数

// 示例:创建一个新的红黑树节点的函数
rb_node* createRbNode(KeyType key) {
    // 动态分配内存来创建一个新的rb_node结构体
    rb_node* newNode = (rb_node*)malloc(sizeof(rb_node));
    if (newNode == NULL) {
        // 如果内存分配失败,打印错误信息并返回NULL
        printf("内存分配失败!\n");
        return NULL;
    }

    // 初始化新节点的各个成员变量
    newNode->leftchild = NULL;
    newNode->parent = NULL;
    newNode->rightchild = NULL;
    newNode->color = RED; // 新创建的节点初始颜色通常设为红色,后续根据规则调整为黑色
    newNode->key = key;

    return newNode;
}

// 示例:简单的红黑树节点打印函数(这里只打印键值和颜色,可根据需求扩展)
void printRbNode(rb_node* node) {
    if (node!= NULL) {
        // 根据节点颜色输出相应信息
        if (node->color == RED) {
            printf("键值: %d, 颜色: 红色\n", node->key);
        } else {
            printf("键值: %d, 颜色: 黑色\n", node->key);
        }
    }
}

4.红黑树的结构

        通常会增加一个头结点,头结点为红色,指向红黑树的根节点。这个头结点在一些操作中可以简化边界情况的处理。

5.红黑树的插入

  • 按照二叉搜索树规则插入新的结点。新插入的结点通常初始颜色为红色,这是因为在红黑树中黑节点至少是红节点的两倍,插入节点的父节点为黑色的概率较大,而此时并不需要作任何调整,因此效率较高。
  • 检测新节点插入后,红黑树的性质是否遭到破坏。如果新插入节点的父节点颜色为红色,就违反了红黑树的性质,此时需要根据叔叔的颜色来做不同的处理。具体情况如下:
  • 情况 1:cur 为红,p 为红,g 为黑,u 存在且为红。解决方式:将 p、u 改为黑,g 改为红,然后把 g 当成 cur,继续向上调整。如果 g 是根节点,调整完成后需要将 g 改成黑色;如果 g 是子树,g 一定有双亲,且 g 的双亲如果是红色,需要继续向上调整。
  • 情况 2:cur 为红,p 为红,g 为黑,u 不存在或者 u 为黑(p 和 cur 都在其父亲节点同一侧)。如果 u 节点不存在,则 cur 一定是新插入节点,因为如果 cur 不是新插入节点,则 cur 和 p 一定有一个节点的颜色是黑色,这样就不满足性质每条路径黑色节点个数相同。解决方式:将 p 改为黑,g 改为红,然后进行旋转。p 为 g 的左孩子,cur 为 p 的左孩子,则进行右单旋转;相反,p 为 g 的右孩子,cur 为 p 的右孩子,则进行左单旋转。
  • 情况 3:cur 为红,p 为红,g 为黑,u 不存在或者 u 为黑(p 和 cur 在其父亲节点的不同侧)。当 u 不存在的情况下,先将 p 节点左旋,再把 p 和 cur 的指向互换,这样就变成情况 2,接着按照情况 2 来处理即可。当 u 存在的情况下,先根据情况 1 调整接着把 p 节点左旋,并交换 p 和 cur 的指向。最后按照情况 2 的处理方式即可。

6.红黑树的验证

  • 检测其是否满足二叉搜索树。可以通过中序遍历红黑树,如果得到的序列是有序的,则满足二叉搜索树的性质。
  • 检测其是否满足红黑树的性质。
  • 验证每个节点要么是红色要么是黑色。
  • 验证根节点是黑色。
  • 验证如果一个节点是红色的,则它的两个孩子节点是黑色的。
  • 验证从任意节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。
  • 验证每个叶子节点都是黑色的。

7.红黑树的删除

        红黑树的删除相对比较复杂,此文只做简单介绍,有兴趣的大家可以去学习学习。

五、红黑树的应用场景

1.数据库索引可以快速地进行插入、删除和查找操作,同时保持索引的有序性。

        红黑树可以用于数据库索引的实现,其自平衡的特性使得在大量数据的情况下,依然能够保持高效的插入、删除和查找操作。例如,当数据发生倾斜时,红黑树不会像二叉树一样变成线性结构,降低查询效率。而且红黑树可以保证索引数据的有序性,减少查询时的 IO 操作次数,提高查询效率。

2.C++ STL 中的 map 和 set 底层实现就是红黑树,可以高效地进行元素的插入、删除和查找操作。

        在 C++ STL 中,map 和 set 的底层实现都是红黑树。map 和 set 所开放的各种操作接口,实际上都是转调红黑树的操作行为。由于红黑树的特性,map 和 set 能够快速地进行元素的插入、删除和查找操作。例如,map 中的每个节点都是一个 key-value 对,通过红黑树的有序性,可以快速地根据 key 查找对应的 value。set 则是关键字的简单集合,每个节点只包含一个关键字,同样利用红黑树的特性实现高效的操作。

3.Linux 进程调度使用红黑树来管理进程的优先级,以便高效地进行进程的调度。

        Linux 内核中的进程调度算法使用红黑树来管理进程的优先级。每个进程都有一个相关的调度实体,包含进程的运行状态和调度属性。红黑树以虚拟运行时间为顺序,将 vruntime 最小的进程放在左边,vruntime 最大的进程放在右边。在进行进程调度时,从最左边取出 vruntime 最小的节点,即最紧迫的进程进行执行。这样可以保证高优先级的进程得到更多的 CPU 时间,同时也保证了低优先级的进程不会被长时间忽略,实现了进程调度的公平性。

4.文件系统使用红黑树来管理文件的目录结构,以便快速地进行文件的查找和访问。

        一些文件系统使用红黑树来管理文件的目录结构。文件系统中的目录树需要高效的查询和更新操作,红黑树正好能满足这些要求。每个目录节点都包含了指向子目录和文件的指针,红黑树保证了目录树的平衡性,使得文件系统的访问性能得到提升。

5.平衡二叉搜索树的实现可以用于实现其他平衡二叉搜索树的算法,如 AVL 树。

        红黑树是一种自平衡的二叉搜索树,它可以用于实现其他平衡二叉搜索树的算法。虽然红黑树对平衡性的要求没有 AVL 树那么严格,但是通过颜色标记和旋转操作,也能保证树的大致平衡。在实现其他平衡二叉搜索树算法时,可以借鉴红黑树的实现思路,提高算法的效率。

6.交易策略开发交易订单管理、交易策略优化、交易风险管理、交易数据分析。

        在交易策略开发中,红黑树有广泛的应用。例如,在交易订单管理中,红黑树可以将订单按照价格或时间等关键指标进行排序,快速地查找、插入和删除订单,对于高频交易和大规模交易系统非常重要。在交易策略优化中,将交易策略的关键指标存储在红黑树中,可以快速地进行查询和比较操作,提高交易策略的执行速度和准确性。在交易风险管理中,将不同的风险指标存储在红黑树中,可以实时地监控和评估交易风险,及时采取相应的风险控制措施。在交易数据分析中,将交易数据按照不同的维度存储在红黑树中,可以方便地进行数据查询、排序和聚合操作,得出交易数据的有用信息和结论。


http://www.kler.cn/a/407490.html

相关文章:

  • [代码随想录Day21打卡] 669. 修剪二叉搜索树 108.将有序数组转换为二叉搜索树 538.把二叉搜索树转换为累加树 总结篇
  • 如何使用ChatGPT整理和收集论文实验数据?
  • 14 go语言(golang) - 并发编程goroutine和channel
  • OpenCV 计算图像清晰度
  • Unreal从入门到精通之如何绘制用于VR的3DUI交互的手柄射线
  • 408代码类复习--八大排序
  • Hive基础笔记
  • 【数据结构-队列】力扣232. 用栈实现队列
  • 洛谷 P1722 矩阵 II C语言 记忆化搜索
  • 对比学习——moco
  • Android 工厂设计模式的使用:咖啡机,可以做拿铁,可以做美式等等。
  • SCTransNet验证测试
  • 解决报错:rror: error:0308010C:digital envelope routines::unsupported
  • 利用软件实现发票的批量查验,并自动截图保存 91发票查验助手
  • 【C++】关于指针Free和链表循环释放的问题
  • websocket消息的实现
  • 【公开笔记】小白学习vue3完整版
  • 智能体来了:构建用于具有结构化输出的内容审核的智能 AI Agent 智能体
  • 【Isaac Sim】加载自带模型或示例时报 Isaac Sim is not responding
  • 联想ThinkServer服务器主要硬件驱动下载
  • 【单片机基础】如何选择合适的低功耗单片机
  • YOLOv11融合[NeurlS2022]递归门控卷积gnconv模块及相关改进思路
  • 10 —— Webpack打包模式
  • Linux使用经验记录
  • 韦东山hal库 使用光敏传感器控制蜂鸣器
  • GoZero对接GPT接口的设计与实现:问题分析与解决