数据结构:红黑树(Red-Black Tree)

目录

从AVL树的“烦恼”说起

如何用“颜色”来定义“大致平衡”?—— 红黑树的五个规则

五个规则如何保证“大致平衡”?

用 C/C++ 代码定义红黑树的结构

定义颜色和节点结构

 定义树的结构和哨兵节点


从AVL树的“烦恼”说起

我们从已经了解的 AVL 树出发。AVL 树的出发点是什么?

是为了解决二叉搜索树(Binary Search Tree, BST)在最坏情况下退化成链表的问题。它的核心思想是:强制平衡。它有一个非常严格的规定:“任意节点左右子树的高度差 ≤ 1”。

这个规定很有效,保证了树的高度始终在 O(logN) 级别,因此查找效率非常高。但它的“烦恼”也来源于此:

📌 为了维持这个严格的平衡,AVL 树的插入和删除操作可能会导致频繁的旋转 (Rotation)。有时候,仅仅插入一个节点,就可能需要从插入点一直回溯到根节点,进行多次旋转。旋转操作本身是有开销的。

既然“绝对平衡”的维护成本有点高,我们能不能稍微“放松”一点要求?我们不追求“完美身高”,只追求“身材匀称”,只要最长路径和最短路径的长度别差得太离谱,那它的查找效率不也能保证在 O(logN) 吗?❓❓❓

这个“放松要求,换取插入/删除时更少操作”的想法,就是红黑树诞生的根本动机。

第一性原理推导:

  1. 目标: 保持树的查找效率,即树的高度维持在 O(logN)。

  2. 现有方案: AVL 树通过严格限制左右子树高度差来实现。

  3. 痛点: 维护严格平衡的成本(旋转次数)较高。

  4. 新思路: 寻找一种弱一点的平衡标准。这个标准必须足够强,以保证树高为 O(logN);又必须足够弱,以减少插入/删除时维护平衡的代价。

红黑树就是这个新思路的杰出实现。它放弃了用“高度”这个属性来做限制,而是引入了一个全新的、更巧妙的属性:颜色 (Color)


如何用“颜色”来定义“大致平衡”?—— 红黑树的五个规则

好,我们现在给每个节点增加一个属性:color,它可以是红色 (RED)或黑色 (BLACK)。通过一些“颜色规则 (Coloring Rules)”来限制树的形态,保证从根到任意叶子的路径长度不会差太多。

1. 二叉搜索树的复杂度依赖高度

  • 查找(Search)复杂度 = O(h),其中 h 是树高。

  • 如果 h 太大(比如退化成链表),复杂度退化为 O(N)

  • 所以我们必须保证 h = O(log N)

2. 最短路径长度与节点数的关系

  • 在一棵二叉树里,最短路径长度(记为 h_min)指从根到某个叶子所经过的边数。

  • 如果所有路径至少有 h_min 个节点(或边),那么这棵树至少包含:2^(h_min) - 1 个节点(这是满二叉树的下界)。

  • 因此:N ≥ 2^(h_min) - 1  ⇒ h_min ≤ log2(N+1)

3. 如果最长路径 ≤ 2 × 最短路径

设:

  • h_min = 最短路径高度

  • h_max = 最长路径高度 = 树的高度

如果能保证:h_max ≤ 2 * h_min

那么结合上面的不等式:h_min ≤ log2(N+1)  ⇒   h_max ≤ 2 * log2(N+1)

于是我们得到了:h_max = O(log N)

我们的最终目标是证明:

一棵红黑树从根到最远叶子节点的路径长度,不会超过到最近叶子节点路径长度的两倍。

如果能证明这一点,就说明树的高度依然是 O(logN),我们的目标就达成了。

我们来一步步推导出这些规则:

1️⃣:每个节点要么是红色,要么是黑色。

这是最基本定义,就像说“游戏里的棋子要么是黑的,要么是白的”。没有它,后续规则无从谈起。

2️⃣:根节点是黑色。

这条规则不是强制性的,但它能简化很多问题。可以把它看作一个“基准”或“锚点”。

如果根节点是红色,它可能会违反其他规则(比如后面会提到的“红色节点的子节点不能是红色”),所以干脆定为黑色,让处理更统一。

3️⃣:所有叶子节点都是黑色。

这里的“叶子节点”比较特殊,它不是指最后一个有数据的节点,而是指其下方的 NULL 指针。

在红黑树的实现中,我们通常会用一个统一的、黑色的哨兵节点 (Sentinel Node) 来代表所有的 NULL

这样做的好处是,处理边界情况(比如一个节点只有一个子节点)时,我们不需要写大量的 if (node->left != NULL) 判断,因为它的 leftright 总是指向一个有效的(哨兵)节点。这纯粹是为了简化代码实现。

至此,我们有了基本的颜色框架。但这些还不足以保证平衡。现在,我们需要引入真正限制树“形状”的规则。

4️⃣:红色节点的子节点必须是黑色的。 (也就是说,不能有两个连续的红色节点)

这是实现“放松平衡”的核心规则之一。如果允许红色节点串在一起(红->红->红...),那这条路径就会被无限制地拉长,树就会失衡。

这条规则强制打断了“红色路径”,确保了从根到叶子的路径上,红色节点不会连续出现。

5️⃣:从任一节点到其每个叶子(NIL 哨兵节点)的所有路径,都包含相同数目的黑色节点。

这是另一条核心规则,也是最难理解但最关键的一条。它定义了一个叫做“黑高 (Black-Height)”的概念。

这条规则强制要求,无论你从一个节点 x 出发,走左边还是走右边,到达终点(叶子)时,路上经过的黑色节点数量必须完全一样。这就像给树建立了一个“黑色的骨架”,这个骨架是完美平衡的。红色节点则可以看作是“填充”在这个黑色骨架之间的节点。


五个规则如何保证“大致平衡”?

现在我们来验证一下,这五条规则是否达成了我们的最终目标:最长路径 <= 2 * 最短路径

📍最短路径:

考虑从根节点到一个叶子节点的最短路径。这条路径上会包含最少的节点。要让节点数最少,我们就应该尽量少放红色节点。

根据规则4,红色节点不能连续,所以最短的路径就是一条纯黑色的路径。这条路径的长度就是这棵树的黑高 (Black-Height),我们记为 bh

📌最长路径:

要让路径最长,我们就应该塞进尽可能多的红色节点。根据规则4,每两个黑色节点之间最多只能插入一个红色节点(黑 -> 红 -> 黑 -> 红 ...)。

由于规则5保证了任何路径上的黑色节点数量都是 bh,那么在最长路径上,我们最多也就能塞进 bh 个红色节点。

所以:

  • 最短路径长度 = bh (全是黑色节点)

  • 最长路径长度 <= bh (黑色节点) + bh (红色节点) = 2 * bh

结论最长路径长度 <= 2 * 最短路径长度

这个结论证明了红黑树的高度始终保持在 O(logN) 级别。我们成功地用五条看似无关的颜色规则,间接实现了树的平衡,而且这个平衡比 AVL 树更“宽松”。这种宽松,将为我们后续的插入和删除操作带来更低的维护成本。


用 C/C++ 代码定义红黑树的结构

好了,理论推导结束。我们现在开始写代码,一步步定义出红黑树的节点和树的结构。

定义颜色和节点结构

首先,我们需要一个 enum 来表示颜色。然后定义节点结构 RBTNode

除了BST原有的 keyleftright 指针,我们还需要 color 属性和一个指向父节点 (parent) 的指针。父节点指针在后续的旋转和调整中非常有用,可以避免复杂的递归或栈来寻找父节点。

// 使用 C 风格的代码,不涉及高级语法和STL#include <stdio.h>// 专有名称: 颜色 (Color)
typedef enum {RED,    // 红色BLACK   // 黑色
} Color;// 专有名称: 红黑树节点 (Red-Black Tree Node)
typedef struct RBTNode {int key;              // 键值Color color;          // 颜色struct RBTNode *left;   // 左子节点指针struct RBTNode *right;  // 右子节点指针struct RBTNode *parent; // 父节点指针
} RBTNode;

这段代码非常基础,就是定义了我们讨论中需要的所有元素:键值、颜色、以及三个方向的指针

 定义树的结构和哨兵节点

根据规则3,我们需要一个黑色的哨兵节点来代表所有的 NULL 叶子。我们可以定义一个全局的哨兵节点 NIL。树本身可以用一个指向根节点的指针来表示。

// 接着上面的代码// 哨兵节点 (Sentinel Node),代表所有的NULL叶子
RBTNode* NIL;// 红黑树结构,本质上是一个指向根节点的指针
typedef struct RedBlackTree {RBTNode* root;
} RedBlackTree;// 初始化函数,用于创建NIL节点
void initializeNIL() {NIL = new RBTNode; // 在C++中用new,在C中用mallocNIL->color = BLACK;NIL->key = 0; // key值无所谓NIL->left = NULL;NIL->right = NULL;NIL->parent = NULL;
}

为什么需要 NIL 哨兵节点?

想象一下,如果没有 NIL,当你想访问 node->left->color 时,你必须先检查 node->left是不是 NULL。而有了 NIL,任何一个节点的 leftright 指针,要么指向一个有效的数据节点,要么指向 NIL

因为 NIL 是一个有确定颜色(黑色)的真实节点,所以你可以安全地访问 node->left->color,这会让代码变得非常整洁。

一个新创建的树,其根节点应该指向 NIL

// 创建一棵空树
RedBlackTree* createRedBlackTree() {RedBlackTree* tree = new RedBlackTree; // C: mallocif (NIL == NULL) {initializeNIL(); // 确保NIL只被初始化一次}tree->root = NIL; // 空树的根指向NILreturn tree;
}

到目前为止,我们已经成功地:

  1. 从第一性原理推导出了红黑树存在的必要性。

  2. 推导出了定义红黑树的5个核心规则。

  3. 证明了这5个规则如何保证树的“大致平衡”。

  4. 用基础的 C/C++ 代码定义了红黑树的节点、树结构以及核心的 NIL 哨兵节点。

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

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

相关文章

Ubuntu22.04安装VMware Tools

文章目录前言安装open-mv-tools前言 本教程使用的版本是Ubuntu22.04.5&#xff0c;由于虚拟机上面的重新安装VMware Tools是灰的&#xff0c;于是自动下载安装open-mv-tools&#xff0c; 安装open-mv-tools 打开终端&#xff0c;更新一下 sudo apt update这一步可能需要先…

DBeaver连接SQL Server时添加驱动后仍提示找不到驱动的解决方法

DBeaver连接SQL Server时添加驱动后仍提示找不到驱动的解决方法 在使用DBeaver连接SQL Server时&#xff0c;即使您已手动添加驱动文件&#xff0c;系统仍提示“找不到驱动”&#xff0c;这通常是由驱动配置错误、版本不兼容或SQL Server设置问题引起的。以下我将逐步为您提供解…

JVM之【类加载系统】

目录 前言 类加载过程 类加载 执行过程 加载阶段 连接阶段 初始化阶段 类加载器 BootstrapClassLoader ExtClassLoader AppClassLoader 类加载器之间的关系 双亲委派机制 核心思想 好处 源码分析 类加载器之间的父子层级关系 双亲委派的体现 前言 上文中提到…

【 限流技术 | 从四大限流算法到Redisson令牌桶实践 】

引言&#xff1a;为什么需要限流&#xff1f;在现代分布式系统中&#xff0c;服务的稳定性是至关重要的。在遇到突发的请求量激增&#xff0c;恶意的用户访问&#xff0c;亦或是请求频率过高给下游服务带来较大压力时&#xff0c;我们常常需要通过缓存、限流、熔断降级、负载均…

深入解析Java NIO多路复用原理与性能优化实践指南

深入解析Java NIO多路复用原理与性能优化实践指南 技术背景与应用场景 在高并发网络编程中&#xff0c;传统的阻塞 I/O 模型往往因每个连接都占用一个线程或一个系统调用而导致线程资源浪费、线程切换开销剧增等问题&#xff0c;难以满足数万甚至数十万并发连接的负载要求。Jav…

目标检测数据集 第006期-基于yolo标注格式的汽车事故检测数据集(含免费分享)

目录 目标检测数据集 第006期-基于yolo标注格式的汽车事故检测数据集(含免费分享) 超实用汽车事故检测数据集分享&#xff0c;助力计算机视觉研究&#xff01; 1、背景 2、数据详情 数据集基本信息 结构组成 标注格式与示例 类标签说明 数据增强情况 3、应用场景 4、…

应用密码学(书籍学习笔记、基础知识) 一

本博客为读《应用密码学》所得笔记 文章目录一、 加密与解密1.2 秘钥Key1.2.1 引入秘钥K1.2.2 加密秘钥K1&#xff0c;解密秘钥K2二、对称算法 VS 公开密钥算法**① 对称算法** - 传统密码算法 **(Symmetric Algorithm) &#x1f511;****② 非对称算法特点** - 公开秘钥算法 *…

【攻防世界】Web_php_include

1.信息收集题目&#xff1a;Web_php_include &#xff1a;PHP文件包含漏洞2.思路&#xff1a;1.代码审计&#xff1a;<?php show_source(__FILE__); echo $_GET[hello]; $page$_GET[page]; while (strstr($page, "php://")) { //在一个字符串中查…

cmake--CPack/deb

deb包的需求 怎么使用cmake把项目的依赖想打包为deb包,把项目的可执行文件和依赖文件打包为deb包,又怎么样配置apt源,让项目在jenkins构建之后,可以通过sudo apt install 下载deb包和安装到任意主机上? 整体流程概览 使用CMake构建项目:确保你的项目可以被CMake正确编译…

七十五、【Linux数据库】部署Redis服务 、 部署LNMP+Redis

Redis 与 LNMP 集成功能概述 Redis 核心功能 内存数据存储:高速读写性能 数据结构丰富:字符串、哈希、列表、集合等 持久化支持:RDB快照和AOF日志 发布订阅:消息队列功能 高可用:主从复制、哨兵模式、集群 LNMP+Redis 集成价值 会话共享:多Web服务器共享Session 数据缓存…

从YOLOv5到RKNN:零冲突转换YOLOv5模型至RK3588 NPU全指南

从YOLOv5到RKNN&#xff1a;零冲突转换YOLOv5模型至RK3588 NPU全指南 在嵌入式AI领域&#xff0c;将训练好的深度学习模型高效部署到边缘设备的NPU&#xff08;神经网络处理器&#xff09;上是提升性能的关键。本文将详细介绍如何在Ubuntu 20.04环境下&#xff0c;将YOLOv5l模型…

DNS的解析过程是怎样的?它基于传输层的什么协议?

问题DNS的解析过程是怎样的&#xff1f;它基于传输层的什么协议&#xff1f;我的回答&#xff1a;DNS解析过程是将域名转换为IP地址的一系列步骤。这个过程涉及多级缓存和查询&#xff1a;首先是浏览器缓存&#xff0c;浏览器会先检查自己的DNS缓存是否有记录。接着是操作系统缓…

模拟互联网大厂Java面试:电商场景下的技术探讨

模拟互联网大厂Java面试&#xff1a;电商场景下的技术探讨 场景概述 在这场模拟面试中&#xff0c;我们设定了一位互联网大厂的面试官与候选人小C之间的对话。面试官严肃专业&#xff0c;而小C则是搞笑的“水货程序员”。通过三轮问答&#xff0c;我们探索了Java技术栈在电商场…

遥感机器学习入门实战教程|Sklearn案例⑤:集成学习方法全览

在机器学习的实际应用中&#xff0c;单一分类器往往存在局限&#xff1a;比如决策树容易过拟合&#xff0c;kNN 对噪声敏感&#xff0c;逻辑回归在高维数据下收敛慢。为了提升整体效果&#xff0c;我们通常会采用 集成学习&#xff08;Ensemble Learning&#xff09;。 这篇文章…

大模型在垂直场景中的创新应用:搜索、推荐、营销与客服的新玩法

1. 引言 背景介绍:简述大模型(如GPT、BERT等)的发展历程及其在AI领域的核心作用,强调其在垂直场景中的潜力。 主题聚焦:说明本文将深入探讨搜索、推荐、营销、客服四大场景,分析大模型带来的创新开发方式。 目的与意义:阐述新玩法如何提升效率、增强用户体验,并推动行业…

华为仓颉语言的class(类)初步

华为仓颉语言的class&#xff08;类&#xff09;初步 class 概念 【官方文档 https://cangjie-lang.cn/docs?url%2F1.0.0%2Fuser_manual%2Fsource_zh_cn%2Fclass_and_interface%2Fclass.html 】 class 是仓颉面向对象体系的核心&#xff0c;用来描述“引用类型”对象。与 s…

健康常识查询系统|基于java和小程序的健康常识查询系统设计与实现(源码+数据库+文档)

健康常识查询系统 目录 基于java和小程序的健康常识查询系统设计与实现 一、前言 二、系统设计 三、系统功能设计 小程序功能设计 后台功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xf…

MySQL的高可用+MHA

即MySQL 主从复制高可用架构&#xff0c;是一套优秀的MySQL 高可用解决方案&#xff0c;由日本 DeNA 公司 youshimaton 开发&#xff0c;主要用于保障 MySQL 数据库在主服务器出现故障时&#xff0c;能快速进行主从切换&#xff0c;减少数据库服务中断时间。其核心特点包括&…

淘宝pc端首页做了哪些性能优化?

淘宝PC端首页作为中国电商领域流量最大的页面之一&#xff0c;其性能优化手段可以说是业界标杆&#xff0c;非常全面和深入。这些优化不是单一技术&#xff0c;而是一个完整的体系。 我们可以从以下几个层面来分析和理解淘宝首页所做的性能优化&#xff1a; 一、核心指标与整体…

让医学数据更直观——MedCalc 23.1.7 最新版使用体验

软件介绍 MedCalc 23.1.7是一款功能强大的生物医学研究统计软件&#xff0c;专为医学科研人员和医疗保健专家设计。它提供了丰富的统计分析工具和方法&#xff0c;旨在帮助用户更好地分析和解释医学数据。以下是该软件的一些主要特点&#xff1a; 一、数据导入和管理 支持导…