红黑树刨析(删除部分)

文章目录

    • 红黑树删除节点情景分析
      • 情景1:删除节点左右子树都为空
        • 情景1.1:删除节点为红色
        • 情景1.2:删除节点为黑色
          • 情况1.2.1:删除节点的兄弟节点是红色
          • 情景1.2.2:删除节点的兄弟节点是黑色
            • 情景1.2.2.1:删除节点的兄弟节点是黑色,兄弟节点的右节点是红色,兄弟节点的左节点是空或者红色
            • 情景1.2.2.2:删除节点的兄弟节点是黑色,兄弟节点的左节点是红色,兄弟节点的右节点是空(右节点是红色按照情况1.2.2.1讨论就可以了)
            • 情景1.2.2.3:删除节点的兄弟节点是黑色,兄弟节点无孩子
      • 情景2:删除节点有一个子树(左子树或者右子树)
      • 情景3:删除节点有两个子树

红黑树删除节点情景分析

情景1:删除节点左右子树都为空

情景1.1:删除节点为红色

那么可以直接将p删除,不影响平衡性

暂时无法在飞书文档外展示此内容

以下这两种情况都符合

在这里插入图片描述
在这里插入图片描述

情景1.2:删除节点为黑色

删除节点为黑色的话就不太平衡了,此时我们就需要看情况讨论了

情况1.2.1:删除节点的兄弟节点是红色

将父亲节点和兄弟节点颜色互换,然后再将父亲节点左旋,此时这就变成了情景1.2.2

在这里插入图片描述

情景1.2.2:删除节点的兄弟节点是黑色

如果兄弟节点为黑色,那么只有两种情况

情景1.2.2.1:删除节点的兄弟节点是黑色,兄弟节点的右节点是红色,兄弟节点的左节点是空或者红色

此时如果我们删除黑色节点p,那么就不平衡了,我们看一下pp - ppr - pprr三个节点,这三个点在一条线上,我们可以借助pprr这个红色节点变成黑色节点来保持平衡。首先让ppppr互换颜色,然后再将pp左旋,那么删除p之后,右边没有黑色节点,直接让pprr变成黑色就行了。在这里插入图片描述

情景1.2.2.2:删除节点的兄弟节点是黑色,兄弟节点的左节点是红色,兄弟节点的右节点是空(右节点是红色按照情况1.2.2.1讨论就可以了)

直接让兄弟右旋,然后将兄弟和侄子互换颜色,就变成了情景1.2.2.1
在这里插入图片描述

情景1.2.2.3:删除节点的兄弟节点是黑色,兄弟节点无孩子

情景1.2.2.3.1:删除节点的兄弟节点是黑色,兄弟节点无孩子,父亲节点是红色

删除节点P之后,左右两边不平衡了,可以直接将父节节点变成黑色,兄弟节点变成红色,这样就平衡了。
在这里插入图片描述
情景1.2.2.3.2:删除节点的兄弟节点是黑色,兄弟节点无孩子,父亲节点是黑色

直接将兄弟节点变成红色,这样就平衡了。但是经过pp的路径上的黑色节点数会少1,这个时候在以pp作为起始点,继续平衡操作,这里可以把pp和ppr当作一个节点pp这样一直向上,直到新的起始点为根节点。
在这里插入图片描述

情景2:删除节点有一个子树(左子树或者右子树)

以下情景不满足红黑树性质不可能出现:
在这里插入图片描述
只有下面两种情况可能出现:

删除节点是黑色,子节点是红色

那么可以直接让子孩子替换p,颜色变成黑色就可以了。
在这里插入图片描述

情景3:删除节点有两个子树

首先找到删除节点的后继节点,再将后继节点和删除节点替换,问题就变成删除替换节点的问题,而且替换节点要么无子树,要么有一个节点,问题就变回了情景1或者情景2。

JDK1.8中hashMap中的删除红黑树节点的源码,我做了一部分的改进,方便阅读。

 final void removeTreeNode(MyHashMap<K, V> map, Node<K, V>[] tab,boolean movable) {/*** 链表的处理*/int n;// 如果当前哈希表为空直接返回if (tab == null || (n = tab.length) == 0)return;// 计算当前节点在hash表的索引位置int index = (n - 1) & hash;// fisrt : t头节点TreeNode<K, V> first = (TreeNode<K, V>) tab[index];// 如果索引位置的红黑树为空if (first == null) {return;}// root:根节点TreeNode<K, V> root = first;// rl : root的左节点TreeNode<K, V> rl;// succ:节点的后继节点TreeNode<K, V> succ = (TreeNode<K, V>) next;// pred:节点的前驱节点TreeNode<K, V> pred = prev;// 如果根节点为空,则当前节点就是头节点,直接删除if (pred == null) {first = succ;tab[index] = succ;// 根节点不为空,当前节点为中间某个节点,删除中间节点} else {// 前驱的后继pred.next = succ;}// 后继的前驱if (succ != null) {succ.prev = pred;}// 如果root的父节点不为空,说明该节点并不是真正的红黑树根节点,需要重新查找根节点if (root.parent != null) {root = root.parent;}// 通过root节点来判断此红黑树是否太小, 如果是太小了则调用untreeify方法转为链表节点并返回// (转链表后就无需再进行下面的红黑树处理)// 太小的判定依据:根节点为null,或者根的右节点为null,或者根的左节点为null,或者根的左节点的左节点为null// 这里并没有遍历整个红黑树去统计节点数是否小于等于阈值6,而是直接判断这几种情况,// 来决定要不要转换为链表,因为这几种情况一般就涵盖了节点数小于6的情况,这样执行效率也会变高if (root == null || root.right == null ||(rl = root.left) == null || rl.left == null) {tab[index] = first.untreeify(map);  // too smallreturn;}/*** 红黑树的处理*/TreeNode<K, V> p = this;TreeNode<K, V> pl = left;TreeNode<K, V> pr = right;// replacement:替换节点TreeNode<K, V> replacement;if (pl != null && pr != null) {// 找到当前节点的后继TreeNode<K, V> s = pr;TreeNode<K, V> sl = s.left;while (sl != null) {s = sl;sl = s.left;}// 交换p和s的颜色boolean c = s.red;s.red = p.red;p.red = c;TreeNode<K, V> sr = s.right;TreeNode<K, V> pp = p.parent;// 如果p的后继节点s恰好是p的右节点,那说明pr没有左节点// 那么就可以直接将pr替换为pif (s == pr) {// 先处理pp.parent = s;p.left = null;p.right = sr;if (sr != null) {sr.parent = p;}// 处理ss.right = p;s.left = pl;pl.parent = s;s.parent = pp;if (pp == null) {root = s;} else if (p == pp.left) {pp.left = s;} else {pp.right = s;}} else {// 将p和s互换TreeNode<K, V> sp = s.parent;p.parent = sp;if (s == sp.left) {sp.left = p;} else {sp.right = p;}p.left = null;p.right = sr;if (sr != null) {sr.parent = p;}s.parent = pp;if (pp == null) {root = s;} else if (p == pp.left) {pp.left = s;} else {pp.right = s;}s.left = pl;s.right = pr;pr.parent = s;}// 如果sr不等于null,那需要p和sr替换掉if (sr != null) {replacement = sr;// 如果sr等于null,此时p无子树,直接删掉就可以} else {replacement = p;}// 走到这里说明pr为null,pl不为null} else if (pl != null) {replacement = pl;// 走到这里说明pl为null,pr不为null} else if (pr != null) {replacement = pr;}// 到这里,说明p的左右节点都为nullelse {replacement = p;}// 删掉当前节点pif (replacement != p) {TreeNode<K, V> pp = replacement.parent = p.parent;// 当p只有一个子树的时候,p的父节点可能为nullif (pp == null) {root = replacement;} else if (p == pp.left) {pp.left = replacement;} else {pp.right = replacement;}// 删掉p节点p.left = p.right = p.parent = null;}// 如果p节点是红色,那不影响树的结构TreeNode<K, V> r = p.red ? root : balanceDeletion(root, replacement);if (replacement == p) {TreeNode<K, V> pp = p.parent;p.parent = null;if (pp != null) {if (p == pp.left) {pp.left = null;} else {pp.right = null;}}}}static <K, V> TreeNode<K, V> balanceDeletion(TreeNode<K, V> root,TreeNode<K, V> x) {TreeNode<K, V> xp, xpl, xpr;while (true) {// 如果x为null或者是根节点,说明已经删除完了if (x == null || x == root) {return root;// 父节点为null,说明是根节点} else if ((xp = x.parent) == null) {x.red = false;return x;// 如果x是红色的,那么直接让它变成黑色的就行了// 因为父节点是黑色的,x节点直接代替他成为黑色的就行了// 这对应情景1.1或情景2} else if (x.red) {x.red = false;return root;// x既不是根节点,也不是红色// x是父亲的左节点} else if ((xpl = xp.left) == x) {// 此时对应于情景1.2.1,父兄换色,然后对x在进行一次平衡if ((xpr = xp.right) != null && xpr.red) {xpr.red = false;xp.red = true;root = rotateLeft(root, xp);xpr = (xp = x.parent) == null ? null : xp.right;}if (xpr == null) {// TODO: 这里应该不可能出现System.out.println("..........");x = xp;} else {TreeNode<K, V> sl = xpr.left, sr = xpr.right;// 此时xpr只能是黑色// 这里if判断成功的可能条件:// 1.sl == null,sr == null (对应情景1.2.2.3)// 2.sl == null,sr == black (不可能)// 3.sl == black,sr == null (不可能)// 4.sl == black,sr == black (不可能)if ((sr == null || !sr.red) &&(sl == null || !sl.red)) {xpr.red = true;x = xp;} else {// 进入这里的可能条件// 1.sl == null,sr == red (对应情景1.2.2.1)// 2.sl == red,sr == null (对应情景1.2.2.2)// 3.sl == red,sr == red (对应情景1.2.2.1)// 4.sl == black,sr == red (不存在)// 4.sl == red,sr == black (不存在)// 条件2if (sr == null) {// 情景1.2.2.2sl.red = false;xpr.red = true;root = rotateRight(root, xpr);xpr = (xp = x.parent) == null ? null : xp.right;}// 此时就变成了场景1.2.2.1if (xpr != null) {// 父兄换色xpr.red = xp.red;if ((sr = xpr.right) != null) {sr.red = false;}}if (xp != null) {xp.red = false;root = rotateLeft(root, xp);}x = root;}}} else {// 如果xpl为红色,那xp和xpl的孩子肯定为黑色if (xpl != null && xpl.red) {xpl.red = false;xp.red = true;root = rotateRight(root, xp);xpl = (xp = x.parent) == null ? null : xp.left;}if (xpl == null) {x = xp;} else {TreeNode<K, V> sl = xpl.left, sr = xpl.right;if ((sl == null || !sl.red) && (sr == null || !sr.red)) {xpl.red = true;x = xp;} else {if (sl == null) {sr.red = false;xpl.red = true;root = rotateLeft(root, xpl);xpl = (xp = x.parent) == null ?null : xp.left;}if (xpl != null) {xpl.red = xp.red;if ((sl = xpl.left) != null)sl.red = false;}if (xp != null) {xp.red = false;root = rotateRight(root, xp);}x = root;}}}}}
}

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

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

相关文章

Cpp学习手册-基础学习

首先你要去网上下载对应的运行软件&#xff0c;先把对应的 C 环境配置好&#xff0c;配置好了我们就可以开始我们的C 学习之旅了。希望通过学习我们能够成为一个比较不错的 C 开发工程师。我也会持续更新 C 知识。 1. C语法基础 当我通过 CLion 工具创建了一个新的 Project 。…

Redis中的 大/热 key问题 ,如何解决(面试版)

big key 什么是 big key? big key&#xff1a;就是指一个内存空间占用比较大的键(Key) 造成的问题&#xff1a; 内存分布不均。在集群模式下&#xff0c;不同 slot分配到不同实例中&#xff0c;如果大 key 都映射到一个实例&#xff0c;则分布不均&#xff0c;查询效率也…

自建电商网站整合Refersion教程

前言&#xff1a;   先介绍一下Refersion有啥用&#xff0c;如果你有一个自己的跨境电商独立站点&#xff0c;想找一些网红帮忙推广销售自己的商品&#xff0c;然后按照转化订单比例给网红支付佣金&#xff0c;这件事情对双方来说透明性和实时性很重要&#xff0c;Refersion就…

C++ | Leetcode C++题解之第382题链表随机节点

题目&#xff1a; 题解&#xff1a; class Solution {ListNode *head;public:Solution(ListNode *head) {this->head head;}int getRandom() {int i 1, ans 0;for (auto node head; node; node node->next) {if (rand() % i 0) { // 1/i 的概率选中&#xff08;替…

Unity URPShader支持多光源处理

//声明变体并且引用文件 #pragma shader_feature _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" //在数据结构体中声明需要使用的数据 struct Attributes {float4 posit…

五种多目标优化算法(NSGA3、MOPSO、MOGWO、NGSA2、SPEA2)性能对比,包含47个多目标测试函数,6种评价指标,MATLAB代码

一、五种多目标算法及六种评价指标简介 多目标灰狼优化算法&#xff08;MOGWO&#xff09;&#xff1a; MOGWO是由Mirjalili等人在2016年提出的&#xff0c;基于灰狼优化算法&#xff08;GWO&#xff09;的多目标版本。它引入了存档机制和改进的头狼选择方式&#xff0c;以处理…

2024-08-30作业

作业2 代码 #include <iostream>using namespace std;class Per { private: string name; int age; double* height; double* weight; public: //有参构造函数 Per(string name,int age,int height,int weight):name(name),age(age),height(new double(height)),weigh…

基于STM32开发的智能家居温度控制系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 系统初始化温度监测与显示风扇/加热器控制Wi-Fi通信与远程监控应用场景 家庭环境的智能温度管理办公楼的节能温控系统常见问题及解决方案 常见问题解决方案结论 1. 引言 随着人们对生活质量…

Flask+LayUI开发手记(六):树型表格的增删改查

树型表格的增删改查功能与数据表格的是完全一致&#xff0c;就是调用layui-form表单组件实现数据输入再提交&#xff0c;比较大的区别是树型节点的编辑&#xff0c;都需要有上级节点的输入&#xff0c;而这个上级节点的展示&#xff0c;必须是以树型方式展示出来。当然&#xf…

使用facebook开源prophet模型预测上证指数etf股价

可以图个乐&#xff0c;没有那么准确&#xff0c;可能还需要更深入的研究分析 蓝线是预测的2024年的走势&#xff0c;绿线是实际走势&#xff0c;红线是历史和未来的分界线。结果上有蛮多差异的。 # 测试预测2024年 coded by luke 伊玛目的门徒 import akshare as ak impor…

信息学奥赛一本通/openjudge Crossing River

题目 一本通题目入口 openjudge题目入口 &#xff08;注&#xff1a;由于一本通题面描述的可能有些欠缺&#xff0c;所以这里的题面采用openjudge英文翻译后的题面&#xff09; 题目分析 首先我们来看样例&#xff0c;为什么样例的结果是17呢?首先观察&#xff0c;“5”和“…

GUI编程04:课堂练习及总结

本节内容视频链接&#xff1a;6、课堂练习讲解及总结_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1DJ411B75F?p6&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 根据前三节学习到的Frame、Panel、Button知识&#xff0c;画出一下窗口界面&#xff1a; 实现代码如下…

Spring Security基于token的极简示例

1 引言 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架&#xff0c;但是用起来有点复杂&#xff0c;为了便于学习理解&#xff0c;下面将用最简洁的配置和示例&#xff0c;展示整个流程。 2 代码 创建一个spring-security…

单图生成 2D 和 3D 人物,高质量图像处理模型 CharacterGen来啦!

CharacterGen引入了一个简化的生成流程和一个图像条件的多视图扩散模型。该模型有效地将输入姿态校准到规范形式&#xff0c;同时保留输入图像的关键属性&#xff0c;从而解决了多样化姿态带来的挑战。 CharacterGen的另一个核心组成部分是基于Transformer的、可泛化的稀疏视图…

kafka 入门

kafka 有分区和副本的概念&#xff0c;partition 3 表示有3个分区&#xff0c;replication 2 表示有2个副本 通过 --describe --topic test命令可以知道 test这个 主题的分区和副本情况&#xff0c;途中的replicas 表示 其他副本分区的情况&#xff0c;如第一条&#xff0c;t…

【spring】学习笔记2:sample、boot功能和组件设计

Spring自带了一个强大的Web框架,名为Spring MVC。Spring MVC的核心 是控制器(controller)的理念。控制器是处理请求并以某种方式进行信息 响应的类。在面向浏览器的应用中,控制器会填充可选的数据模型并将请求 传递给一个视图,以便于生成返回给浏览器的HTML。在pom.xml文件…

免费批量Excel文件合并、拆分软件

软件介绍 下载地址&#xff1a;https://pan.quark.cn/s/ae860a4e2ccb 1.多个XLS或XLSX格式EXCEL文件合并&#xff0c;合并后可使用数据透视表进行相关操作。 2.自动合并多个EXCEL文件的第一个工作表&#xff0c;并汇总成一张表&#xff0c;可根据所有列标题需要指定需要的列。 …

Ethernet 测试系列(1)-- 物理层测试::IOP Test::Link-up time

车载以太网物理层IOP测试&#xff0c;即互操作性测试&#xff08;Interop- erability Tests&#xff09;&#xff0c;用于验证车载以太网PHY&#xff08;通常也称为收发器&#xff09;的可靠性和检查PHY能否在给定的有限时间内建立稳定的链路;还用于车载以太网PHY的诊断&#x…

Unity编辑器扩展之Hierarchy面板扩展

内容将会持续更新&#xff0c;有错误的地方欢迎指正&#xff0c;谢谢! Unity编辑器扩展之Hierarchy面板扩展 TechX 坚持将创新的科技带给世界&#xff01; 拥有更好的学习体验 —— 不断努力&#xff0c;不断进步&#xff0c;不断探索 TechX —— 心探索、心进取&#xff…

JavaScript学习文档(11):Window对象、本地存储、数组中一些方法、学生就业统计表案例

目录 一、Window对象 1、BOM(浏览器对象模型) 2、定时器-延时函数 3、 JS执行机制 &#xff08;1&#xff09;同步任务&#xff1a; &#xff08;2&#xff09;异步任务&#xff1a; 4、location对象 &#xff08;1&#xff09;5秒钟后跳转页面 5、navigator对象 6、…