C++-红黑树

1、红黑树的概念

        红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路 径会比其他路径长出俩倍,因而是接近平衡的。

红黑树图像,如图所示:

2、红黑树的性质

  1. 颜色属性:每个节点不是红色就是黑色

  2. 根节点属性:根节点必须是黑色的

  3. 红色节点属性:如果一个节点是红色的,则它的两个子节点必须是黑色的(即不能有两个连续的红色节点)

  4. 黑色高度属性:对于每个节点,从该节点到其所有后代叶节点的简单路径上,包含相同数目的黑色节点

  5. 叶子节点属性:每个叶子节点(NIL节点,空节点)都是黑色的

        这些性质保证了红黑树的关键特性:最长路径不超过最短路径的两倍,从而保证了树的平衡性。

3、红黑树的实现

1.节点结构定义

template<class T>
struct RBTreeNode {RBTreeNode<T>* _left;    // 左子节点RBTreeNode<T>* _right;   // 右子节点RBTreeNode<T>* _parent;  // 父节点T _kv;                   // 键值对数据Colour _col;             // 节点颜色(RED或BLACK)RBTreeNode(const T& kv):_left(nullptr), _right(nullptr), _parent(nullptr),_kv(kv), _col(RED) {}
};

        节点包含左右子节点指针、父节点指针、存储的数据以及颜色标记。新节点默认为红色。

2. 插入操作 (Insert)

        功能:向红黑树中插入一个新节点,并保持红黑树的性质。

步骤

  1. 如果树为空,创建新节点作为根节点,并设为黑色

  2. 按照二叉搜索树的规则找到插入位置

  3. 创建新节点(红色)并插入到正确位置

  4. 检查并修复红黑树性质:

    • 如果父节点是黑色,无需处理

    • 如果父节点是红色,需要调整:

      • 情况1:叔叔节点是红色 - 变色处理

      • 情况2:叔叔节点是黑色或不存在 - 旋转处理

  5. 确保根节点始终为黑色

约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

  • 情况一: cur为红,p为红,g为黑,u存在且为红

cur和p均为红,违反了性质三,此处能否将p直接改为黑?

        解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。

  • 情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑

p为g的左孩子,cur为p的左孩子,则进行右单旋转;

相反, p为g的右孩子,cur为p的右孩子,则进行左单旋转

p、g变色--p变黑,g变红

  • 情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑

p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;

相反, p为g的右孩子,cur为p的左孩子,则针对p做右单旋转

则转换成了情况2

针对每种情况进行相应的处理即可。

bool Insert(const T& kv)
{if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;//u存在且为红if (uncle && uncle->_col == RED){//变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上处理cur = grandfather;parent = cur->_parent;}else //u不存在或存在且为黑{if (cur == parent->_left){//			g//		p//	cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//			g//		p//			cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else // parent == grandfather->_right{Node* uncle = grandfather->_left;//u存在且为红if (uncle && uncle->_col == RED){//变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上处理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){// g//	  p//       cRotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{// g//	  p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;
}

3. 左旋操作 (RotateL)

        功能:以parent节点为支点进行左旋。

操作

  1. 将cur的左子节点作为parent的右子节点

  2. 将parent作为cur的左子节点

  3. 更新各个节点的父指针

  4. 处理根节点情况

//左单旋
void RotateL(Node* parent)
{Node* cur = parent->_right;parent->_right = cur->_left;if (cur->_left){cur->_left->_parent = parent;}cur->_left = parent;Node* pparent = parent->_parent;parent->_parent = cur;if (pparent == nullptr){_root = cur;cur->_parent = nullptr;}else{if (pparent->_left == parent){pparent->_left = cur;}else if (pparent->_right == parent){pparent->_right = cur;}cur->_parent = pparent;}
}

4.右旋操作 (RotateR)

        功能:以parent节点为支点进行右旋。

操作

  1. 将cur的右子节点作为parent的左子节点

  2. 将parent作为cur的右子节点

  3. 更新各个节点的父指针

  4. 处理根节点情况

//右单旋
void RotateR(Node* parent)
{Node* cur = parent->_left;parent->_left = cur->_right;if (cur->_right){cur->_right->_parent = parent;}Node* pparent = parent->_parent;cur->_right = parent;parent->_parent = cur;if (pparent == nullptr){_root = cur;cur->_parent = nullptr;}else{if (pparent->_left == parent){pparent->_left = cur;}else{pparent->_right = cur;}cur->_parent = pparent;}
}

5. 红黑树验证 (IsBalance)

功能:验证红黑树是否满足以下性质:

  1. 每个节点要么是红色,要么是黑色

  2. 根节点是黑色

  3. 每个叶子节点(NIL节点)是黑色

  4. 不能有两个连续的红色节点

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

实现

  1. IsBalance()是公开接口,调用IsBalance(_root)

  2. IsBalance(Node* root)验证根节点是否为黑色

  3. CheckColour()递归检查:

    • 计算每条路径的黑色节点数是否一致

    • 检查是否有连续的红色节点

bool CheckColour(Node* root, int blacknum, int benchmark)
{//递归停止条件if (root == nullptr){if (blacknum != benchmark)return false;return true;}if (root->_col == BLACK){++blacknum;}if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << root->_kv.first << "出现连续的红色节点" << endl;return false;}return CheckColour(root->_left, blacknum, benchmark)&& CheckColour(root->_right, blacknum, benchmark);
}bool IsBalance()
{return IsBalance(_root);
}bool IsBalance(Node* root)
{if (root == nullptr)return true;if (root->_col != BLACK){return false;}//基准值int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)++benchmark;cur = cur->_left;}return CheckColour(root, 0, benchmark);
}

4、红黑树与AVL树的比较

特性红黑树AVL树
平衡标准近似平衡(最长路径≤2倍最短)严格平衡(高度差≤1)
查询效率O(log n)O(log n)
插入/删除效率相对较高(旋转次数少)相对较低(旋转次数多)
实现复杂度中等较高
适用场景频繁插入删除的场景查询为主、很少修改的场景

5、 红黑树的应用

  1. C++ STL:map、set、multimap、multiset

  2. Java集合框架:TreeMap、TreeSet

  3. Linux内核:进程调度、内存管理等

  4. 其他:数据库索引、文件系统等

6、 总结

        红黑树是一种高效的平衡二叉搜索树,它通过颜色标记和旋转操作维持树的近似平衡。相比于AVL树,红黑树在插入和删除操作上更为高效,适合需要频繁修改的场景。理解红黑树的原理和实现,对于深入掌握STL容器和许多系统级的数据结构都有重要意义。

         红黑树的设计体现了计算机科学中典型的时空权衡思想:通过放宽平衡条件来减少维护平衡的开销,同时仍能保证较好的性能。这种思想在许多算法和数据结构中都有体现,值得深入理解和掌握。

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

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

相关文章

在Python中避免使用`None`表示特殊情况:函数返回值与异常处理的最佳实践 (Effective Python 第20条)

在Python编程中&#xff0c;函数的设计与实现直接影响代码的可读性、可维护性和健壮性。一个常见的问题是如何处理函数的返回值&#xff0c;尤其是在需要表示某种特殊或异常情况时。许多开发者习惯性地使用None来表示这些特殊情况&#xff0c;但这种方法往往会导致意想不到的错…

从反射到方法句柄:深入探索Java动态编程的终极解决方案

&#x1f31f; 你好&#xff0c;我是 励志成为糕手 &#xff01; &#x1f30c; 在代码的宇宙中&#xff0c;我是那个追逐优雅与性能的星际旅人。 ✨ 每一行代码都是我种下的星光&#xff0c;在逻辑的土壤里生长成璀璨的银河&#xff1b; &#x1f6e0;️ 每一个算法都是我绘制…

算法_python_学习记录_01

人心的成见是一座大山。一旦有山挡在面前&#xff0c;则很难到达下一站。所需要做的&#xff0c;是穿过这座山。 偶然间看了一个视频&#xff0c;说的是EMASMA的自动交易策略&#xff0c;这个视频做的很用心&#xff0c;在入场的时间不仅要看EMA的金叉&#xff0c;还需要看其他…

机器翻译中的语言学基础详解(包括包括语法、句法和语义学等)

文章目录一、语法&#xff08;Grammar&#xff09;&#xff1a;语言规则的底层框架1.1 传统语法理论的应用1.2 生成语法&#xff08;Generative Grammar&#xff09;1.3 依存语法&#xff08;Dependency Grammar&#xff09;二、句法&#xff08;Syntax&#xff09;&#xff1a…

MQTT:Dashboard访问授权

目录一、认证1.1 创建认证器1.2 多认证器二、授权2.1 ACL文件授权配置2.2 使用内置数据库授权配置一、认证 认证&#xff1a;就是验证客户端的身份。 1.1 创建认证器 选择认证方式配置数据源配置数据源的相关参数 认证器创建之后&#xff0c;在使用客户端连接Dashboard时&am…

Serper注册无反应

google邮箱才行&#xff0c;163邮箱注册无反应&#xff0c;其他邮箱没试过 在尝试websailor系列的时候&#xff0c;需要注册serper&#xff0c;获取Google Search Key serper.dev/dashboard

聊聊经常用的微服务

聊聊微服务 架构演变 单体架构&#xff1a; All in One&#xff0c;所有的功能模块都在一个工程里。 SOA架构&#xff1a; 这个架构当不当正不正&#xff0c;对于现在来说&#xff0c;有点老&#xff0c;甚至需要ESB&#xff0c;WebService之类的&#xff0c;基本不会使用了。…

第十四届蓝桥杯青少年组省赛 编程题真题题解

明天我就要考蓝桥杯省赛了&#xff0c;本蒟蒻已瑟瑟发抖&#xff0c;所以现在写一篇文章。 题目分别为&#xff1a; 1.​​​​​​B4270 [蓝桥杯青少年组省赛 2023] 特殊运算符 2.B4271 [蓝桥杯青少年组省赛 2023] 四叶玫瑰数 3.B4272 [蓝桥杯青少年组省赛 2023] 质因数的…

HTML全景效果实现

我将为您创建一个精美的360度全景效果页面&#xff0c;使用Three.js库实现沉浸式全景体验&#xff0c;并提供用户友好的控制界面&#xff0c;完整代码看文章末尾。 设计思路 使用Three.js创建全景球体 添加控制面板用于切换不同场景 实现自动旋转和手动控制选项 添加加载状…

Python 属性描述符(描述符用法建议)

描述符用法建议 下面根据刚刚论述的描述符特征给出一些实用的结论。 使用特性以保持简单 内置的 property 类创建的其实是覆盖型描述符&#xff0c;__set__ 方法和 __get__ 方法都实现了&#xff0c;即便不定义设值方法也是如此。特性的 __set__ 方法默认抛出 AttributeError: …

Milvus 向量数据库内存使用相关了解

1、支持 MMap 的数据存储在 Milvus 中&#xff0c;内存映射文件允许将文件内容直接映射到内存中。这一功能提高了内存效率&#xff0c;尤其是在可用内存稀缺但完全加载数据不可行的情况下。这种优化机制可以增加数据容量&#xff0c;同时在一定限度内确保性能&#xff1b;但当数…

C++编程之旅-- -- --默认成员函数(全详解)

目录前言构造函数构造函数形式&#xff1a;构造函数的特性&#xff1a;explicit关键字析构函数析构函数的概念析构函数的特性含有类类型的成员变量的类析构函数的调用拷贝构造函数拷贝构造函数的概念拷贝构造函数的特性浅拷贝和深拷贝&#xff1a;拷贝构造函数典型调用场景&…

Linux网络编程:TCP的远程多线程命令执行

目录 前言&#xff1a; 一、前文补充 二、服务端的修改 三、Command类的新增 前言&#xff1a; 好久不见&#xff0c;最近忙于其他事情&#xff0c;就耽误了咱们的Linux的网络部分的学习。 今天咱们先来给之前所学的TCP的部分进行一个首尾工作&#xff0c;主要是给大家介绍…

重学React(三):状态管理

背景&#xff1a; 继续跟着官网的流程往后学&#xff0c;之前已经整理了描述UI以及添加交互两个模块&#xff0c;总体来说还是收获不小的&#xff0c;至少我一个表面上用了四五年React的前端小卡拉米对React的使用都有了新的认知。接下来就到了状态管理&#xff08;React特地加…

java web项目入门了解

目录一、项目流程1. 使用servle2. 使用框架二、了解java web项目构造1. 项目目录结构2. 查看页面访问顺序3. 发起请求&#xff1a;jqueryajax4. 接受参数5. JSONJSON 数组三、get和post请求区别一、项目流程 1. 使用servle 有客户端和服务端&#xff0c;客户端和服务端进行交…

网络资源模板--基于Android Studio 实现的日记本App

目录 一、测试环境说明 二、项目简介 三、项目演示 四、部设计详情&#xff08;部分) 创建修改页面 五、项目源码 一、测试环境说明 电脑环境 Windows 11 编写语言 JAVA 开发软件 Android Studio (2020) 开发软件只要大于等于测试版本即可(近几年官网直接下载也可…

GO的启动流程(GMP模型/内存)

目录第一部分&#xff1a;程序编译第二部分&#xff1a;函数解读1&#xff09;Golang 核心初始化过程2&#xff09;创建第一个协程3&#xff09;启动系统调度4&#xff09;跳转main函数5&#xff09;总结第三部分&#xff1a;GMP模型Goroutine流程解读第四部分&#xff1a;内存…

OLTP与OLAP:实时处理与深度分析的较量

OLTP&#xff08;Online Transaction Processing&#xff09;定义&#xff1a;OLTP 系统主要用于管理事务性应用程序的数据。这类系统需要支持大量的短时、快速的交互式事务&#xff0c;比如银行交易、在线购物订单等。特点&#xff1a;实时处理&#xff1a;OLTP 系统要求对数据…

数据安全与隐私保护:企业级防护策略与技术实现

引言&#xff1a;数据安全的新时代挑战在数字化转型加速的今天&#xff0c;数据已成为企业最核心的资产。然而&#xff0c;数据泄露事件频发&#xff0c;据 IBM《2024 年数据泄露成本报告》显示&#xff0c;全球数据泄露平均成本已达445 万美元&#xff0c;较 2020 年增长了 15…

AI_RAG

一.为什么需要RAG&#xff08;AI幻觉&#xff09;大模型LLM在某些情况下给出的回答很可能错误的&#xff0c;涉及虚构甚至是故意欺骗的信息。二.什么是RAGRAG是一种结合“信息检索”和“文本生成”的技术&#xff0c;旨在提升生成式AI模型的准确性和可靠性。它通过以下两个核心…