【数据结构】红黑树原理及实现

目录

  • 一. 红黑树的概念
    • 1. 红黑树的规则
      • 思考
    • 2. 红黑树的效率
  • 二.红黑树的实现
    • 1. 红黑树的结构
    • 2. 红黑树的插入
    • 3. 红黑树的平衡调整
      • 情况1:变色
      • 情况2:单旋+变色
      • 情况3:双旋+变色
    • 4. 红黑树插入及平衡调整代码实现
    • 5.红黑树的验证

一. 红黑树的概念

二叉树的优化,每个节点有一个变量表示结点的颜色,可以是红色或黑色,通过对节点颜色的约束,可以确保没有路径比其他路径长出两倍,接近平衡。

1. 红黑树的规则

  1. 每个节点不是红色就是黑色
  2. 根节点是黑色
  3. 不能有相连的红色节点,也就是说一个节点如果是红色,他的孩子一定是黑色
  4. 对于任意一条从根节点到空的路径,黑色节点的数量都相同
    在这里插入图片描述

思考

为什么红黑树的最长路径不超过最短路径的两倍?
每条路径都有相同数量的黑色节点,假设一条路上全是黑色,这是最短的路径长度为h,由于不能有相连的红色节点,最长的路径是一个红一个黑的路径排列,长度为2h,所以对任意一条路径长度x来说,h<=x<=2h

2. 红黑树的效率

假设节点数量为N,h为最短路径长度,2h-1 <= N < 22*h-1, 分析可得h=logN,由此可见最快的查找为logN,最坏的情况下高度为2h,查找效率也还是2*logN,所以红黑树的查找效率为O(logN)

二.红黑树的实现

1. 红黑树的结构

用bool类型来定义树的颜色,
定义parent,left和right指针方便后续的调整

typedef bool RBTreeColor;
const  RBTreeColor RBTreeRed = true;
const  RBTreeColor RBTreeBlack = false;template <class K,class V>
struct RBTreeNode
{typedef RBTreeNode Node;pair<K, V> _kv;Node* _parent;Node* _left;Node* _right;RBTreeColor _color;RBTreeNode(const pair<K,V>& kv):_kv(kv),_parent(nullptr),_left(nullptr),_right(nullptr),_color(RBTreeRed){}
};
template <class K, class V>
class RBTree
{
public:typedef RBTreeNode Node;private:Node*  _root=nullptr;
};

2. 红黑树的插入

  1. 先按照二叉树的规则进行插入
  2. 如果是空树插入,则新插入的节点为黑色,成为树的根,否则新插入的节点为红色,这是为了维护每条路径上的黑色节点数量相同这个规则
  3. 非空树插入后,新增节点为红色,如果父节点为黑色,不违反插入规则,插入结束
  4. 非空树插入后,新增节点为红色,如果父节点也为红色,违反插入规则,需要进行平衡调整

3. 红黑树的平衡调整

走到了平衡这一步,一定是因为出现了相连的两个红色节点,新插入节点c(cur)为红色,插入节点的父节点p(parent)一定也为红色,父节点的父节点g(grandparent)一定是黑色,这三个节点的颜色都是固定的,关键看父节点兄弟节点u(uncle)的情况,根据u的情况,可以分出几种平衡调整规则

情况1:变色

u存在且为红,p和u变成黑色,g变成红色,把g当成新的c,继续向上更新
分析:以g为根的左右子树的黑色节点数是一定的,把g变成红色,p和u变成黑色,不会影响左右子树的黑色节点数,以g为根的树的黑色节点数的也没有改变,由于g的父节点也是红色,所以需要把g当成新的c节点继续向上更新
在这里插入图片描述

情况2:单旋+变色

u不存在或者存在且为黑
u不存在时,c一定是新增节点;u存在且为黑时,c一定不是新增节点,符合情况一
分析:这种情况必须使p的变成黑色,g变成红色,然后旋转,让p变成这棵树新的根,由于新的根是黑色,不管他的父节点是什么,都符合红黑树的规则,插入结束
关于旋转的具体分析参照AVL数章节
在这里插入图片描述
当p是g的左孩子,c是p的左孩子,以g为旋转点,进行右单旋,p变成新的根,

在这里插入图片描述
当p是g的右孩子,c是p的右孩子,以g为旋转点,进行左单旋,p变成新的根,
在这里插入图片描述
在这里插入图片描述

情况3:双旋+变色

u不存在或者存在且为黑
u不存在时,c一定是新增节点;u存在且为黑时,c一定不是新增节点,符合情况一
分析:和情况二类似,根据情况单旋变双旋
在这里插入图片描述
当p是g的左孩子,c是p的右孩子,先以p为旋转点,进行左单旋,然后以g为旋转点,进行右单旋,c变黑,g变红,c变成这棵树新的根

在这里插入图片描述当p是g的右孩子,c是p的左孩子,先以p为旋转点,进行右单旋,然后以g为旋转点,进行左单旋,c变黑,g变红,c变成这棵树新的根
在这里插入图片描述
在这里插入图片描述

4. 红黑树插入及平衡调整代码实现

旋转代码参考AVL章节—>AVL原理及实现

};
template <class K, class V>
bool RBTree < K,V>::Insert(const pair<K, V>& kv)
{if (_root == nullptr){_root = new Node(kv);_root->_color = RBTreeBlack;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);//判断新增节点使父节点左还是右if (parent->_kv.first < kv.first)parent->_right = cur;elseparent->_left = cur;cur->_parent = parent;while (parent && parent->_color == RBTreeRed){Node* grandfather = parent->_parent;//  g//p   u//p为g的左代码实现if (parent == grandfather->_left){Node* uncle = grandfather->_right;//  u存在且为红,p和u变黑,g变红,改变cur和p的指向,继续向上变;if (uncle && uncle->_color == RBTreeRed){parent->_color = uncle->_color = RBTreeBlack;grandfather->_color = RBTreeRed;cur = grandfather;parent = grandfather->_parent;}// u不存在或存在且为黑else{//      g//    p    u//  c//单旋加变色,c为p的左,   p变成新的根,p变黑,g变红if (cur == parent->_left){RorateR(grandfather);parent->_color = RBTreeBlack;grandfather->_color = RBTreeRed;}//        g//    p         u//       c//双旋加变色,c为p的右,else{RorateLR(grandfather);cur->_color = RBTreeBlack;grandfather->_color = RBTreeRed;}}}else{ //这里是p为g的情}return true;}

5.红黑树的验证

这里需要回忆一下红黑树四条规则,只要满足了这四条规则,就符合红黑树的标准
在这里插入图片描述

  1. 节点颜色用bool值标记,天然满足这个规则
  2. 检查根节点是否是黑色即可
  3. 一个节点可能不止一个孩子,但一个节点只能有一个父亲,所以遇到一个红色节点就查他的父亲是否是黑色,满足这点就满足了第三点
  4. 可以先序计算任意一条路径黑色节点的数量 ,然后让每个路径的黑色节点数和他比较,如果全都相等就满足第四条规则
template <class K, class V>
bool RBTree < K, V>::check(Node* root, int count, const int blackNum)
{// 检查到空树就比较黑色节点数if (root == nullptr){if (count == blackNum)return true;elsereturn false;}//如果有连续的红色节点,返回false,// 这里的root不会是根节点,如果是根节点在IsBanlance函数就已经检测出并返回了,所以每一个红色节点的父节点都不会为空if (root->_color == RBTreeRed && root->_parent->_color == RBTreeRed)return false;//遇到黑色节点if (root->_color == RBTreeBlack)count++;return check(root->_left, count, blackNum) && check(root->_right, count, blackNum);
}
template <class K, class V>
bool RBTree < K, V>::IsBanlance(Node* root)
{if (root == nullptr)return true;if (root->_color == RBTreeRed)return false;Node* cur = root;int count = 0;while (cur){if (cur->_color == RBTreeBlack)count++;cur = cur->_left;}
}

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

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

相关文章

时间复杂度分析

复杂度分析的必要性&#xff1a; 当给我们一段代码时&#xff0c;我们是以什么准则来判断代码效率的高低呢&#xff1f;每一段代码都会消耗一段时间&#xff0c;或占据一段数据空间&#xff0c;那么自然是在实现相同功能的情况下&#xff0c;代码所耗时间最少&#xff0c;所占…

L1-1、Prompt 是什么?为什么它能“控制 AI”?

*Prompt 入门 L1-1 想象一下&#xff0c;你只需输入一句话&#xff0c;AI 就能自动为你写一篇文案、生成一份报告、甚至规划你的创业计划。这种“对话即编程”的背后魔法&#xff0c;就是 Prompt 的力量。 &#x1f50d; 一、Prompt 的定义与由来 Prompt&#xff08;提示词&am…

微信小程序文章管理系统开发实现

概述 在内容为王的互联网时代&#xff0c;高效的文章管理系统成为各类平台的刚需。幽络源平台今日分享一款基于SSM框架开发的微信小程序文章管理系统完整解决方案&#xff0c;该系统实现了多角色内容管理、智能分类、互动交流等功能。 主要内容 一、用户端功能模块 ​​多角…

【Python-Day 5】Python 格式化输出实战:%、format()、f-string 对比与最佳实践

Langchain系列文章目录 01-玩转LangChain&#xff1a;从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块&#xff1a;四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain&#xff1a;从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…

R7周:糖尿病预测模型优化探索

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客 &#x1f356; 原作者&#xff1a;K同学啊 一、数据预处理 1.设置GPU import torch.nn.functional as F import torch.nn as nn import torch, torchvisiondevice torch.device("cuda"…

使用Tortoise-ORM和FastAPI构建评论系统

title: 使用Tortoise-ORM和FastAPI构建评论系统 date: 2025/04/25 21:37:36 updated: 2025/04/25 21:37:36 author: cmdragon excerpt: 在models.py中定义了Comment模型,包含id、content、created_at、updated_at字段,并与User和Article模型建立外键关系。schemas.py中定义了…

【VS Code】如何使用SSH打开远程服务器Docker上的项目或文件夹

要在VS Code中使用SSH打开远程服务器Docker上的项目或文件夹&#xff0c;您需要结合使用VS Code的Remote - SSH扩展和Docker的远程访问功能。以下是详细步骤&#xff1a; 安装VS Code Remote - SSH扩展 打开VS Code。点击左侧活动栏的扩展图标&#xff08;或使用快捷键CtrlShif…

NHANES指标推荐:PLP

文章题目&#xff1a;Association of pyridoxal 5-phosphate (PLP) with lipid profiles: a population-based cohort study DOI&#xff1a;10.3389/fnut.2025.1545301 中文标题&#xff1a;5-磷酸吡哆醛 (PLP) 与血脂谱的关系&#xff1a;一项基于人群的队列研究 发表杂志&am…

MySQL 详解之备份与恢复策略:数据安全的最后一道防线

在任何信息系统中,数据都是最宝贵的资产。数据的丢失可能源于多种原因:硬件故障、人为误操作、软件 Bug、恶意攻击,甚至自然灾害。一旦发生数据丢失,如果没有有效的备份和恢复机制,后果可能是灾难性的,可能导致业务中断、经济损失甚至法律责任。 数据库备份与恢复,正是…

2026《数据结构》考研复习笔记五(栈、队列)

栈、队列 一、栈1.卡特兰数2.不合法的出栈序列 二、队列1.循环队列2.输入输出受限队列&#xff08;四个数1234&#xff09; 三、算法1.栈在括号匹配中的应用2.中缀表达式求值&#xff08;通过转化为后缀表达式再后缀表达式求值&#xff09;3.中缀表达式转化为后缀表达式4.后缀表…

深入解析微软MarkitDown:原理、应用与二次开发指南

一、项目背景与技术定位 微软开源的MarkitDown并非简单的又一个Markdown解析器&#xff0c;而是针对现代文档处理需求设计的工具链核心组件。该项目诞生于微软内部大规模文档系统的开发实践&#xff0c;旨在解决以下技术痛点&#xff1a; 大规模文档处理性能&#xff1a;能够高…

pyinstaller打包paddleocr发生错误解决

python环境是3.9&#xff0c;github paddleocr v2.10.0。 一个非常简单的案例如下&#xff0c;打包时发生错误。 import requests from paddleocr import PaddleOCR if __name__ "__main__":paddleocr_ocr PaddleOCR(use_angle_clsTrue, langch,det_model_dirmode…

算法之回溯法

回溯法 回溯法定义与概念核心思想回溯法的一般框架伪代码表示C语言实现框架 回溯法的优化技巧剪枝策略实现剪枝的C语言示例记忆化搜索 案例分析N皇后问题子集和问题全排列问题寻路问题 回溯法的可视化理解决策树状态空间树回溯过程 回溯法与其他算法的比较回溯法与动态规划的区…

命令行指引的尝试

效果 步骤 首先初始化一个空的项目&#xff0c;然后安装一些依赖 npm init -y npm install inquirer execa chalk ora至于这些依赖是干嘛的&#xff0c;如下图所示&#xff1a; 然后再 package.json 中补充一个 bin 然后再根目录下新建一个 index.js , 其中的内容如下 #!/…

探秘LLM推理模型:hidden states中藏着的self verification的“钥匙”

推理模型在数学和逻辑推理等任务中表现出色&#xff0c;但常出现过度推理的情况。本文研究发现&#xff0c;推理模型的隐藏状态编码了答案正确性信息&#xff0c;利用这一信息可提升推理效率。想知道具体如何实现吗&#xff1f;快来一起来了解吧&#xff01; 论文标题 Reasoni…

流量抓取工具(wireshark)

协议 TCP/IP协议簇 网络接口层&#xff08;没有特定的协议&#xff09;PPPOE 物理层数据链路层 网络层: IP(v4/v6) ARP&#xff08;地址解析协议) RARP ICMP(Internet控制报文协议) IGMP传输层&#xff1a;TCP(传输控制协议&#xff09;UDP&#xff08;用户数据报协议)应用层…

.NET仓储层在 using 块中创建 SqlSugarClient 的风险

如题&#xff0c;先看代码示例 using 块的使用 public ISugarQueryable<T> GetSet(Expression<Func<T, bool>> whereExpression null) {using (SqlSugarClient dbClient SqlSugarInstance.GetInstance()){var query dbClient.Queryable<T>();if (w…

C语言----函数栈帧讲解

目录 1.函数栈帧是什么? 2. 理解函数栈帧能解决什么问题 3、函数栈帧的创建和销毁具体过程 3.1 什么是栈 3.2 认识相关寄存器和汇编指令 3.3函数栈帧的创建和销毁 3.3.1 预备知识 3.3.2 函数的调用堆栈 3.3.3 准备环境 3.3.4 转到反汇编 3.3.5 函数栈帧的创建 3.3…

代码随想录学习笔记---二叉树

学习目标&#xff1a; 学习代码随想录–二叉树 每天学习1道,复习两道 学习内容&#xff1a; 2025.4.7 复习内容: 24. 两两交换链表中的节点 25. 最大二叉树 学习内容 26. 合并二叉树 2025.4.8 复习内容: 27. 二分查找 28. 合并二叉树 29. 27. 移除元素 学习内容: 30. 二叉…

Git ——提交至github,Vercel拉取,更新不了项目的问题解决

首先因为github上有个错误 1 failing check Vercel - No GitHub account was found matching the commit author email address 发现好像是vercel拉取不了项目&#xff0c;vercel登录的邮箱与我此次提交更改的邮箱不匹配&#xff0c;查看Git的user确实如此&#xff08;之前的…