红黑树剖析(插入部分)

news2024/11/16 22:01:09

文章目录

    • 红黑树插入节点情景分析
      • 情景1:红黑树为空树
      • 情景2:插入节点的Key已存在
      • 情景3:插入节点的父节点为黑色节点
      • 情景4:插入节点的父节点为红色
        • 情景4.1 叔叔节点存在并且为红色节点
        • 情景4.2 叔叔节点存在而且是黑色节点
        • 情景4.3 叔叔节点不存在
          • 4.3.1 插入节点是父亲节点的左孩子
          • 4.3.2 插入节点是父亲节点的右孩子
      • 插入源代码

插入的节点的颜色都是红色的,因为插入红色对树的影响较小,如果插入的是黑色节点,那么必须要左复杂的平衡操作。

红黑树插入节点情景分析

情景1:红黑树为空树

那么直接让插入节点x 作为根节点,并且设置成黑色。

情景2:插入节点的Key已存在

直接更新节点为插入结点的值。

情景3:插入节点的父节点为黑色节点

由于插入的节点是红色的,当插入节点的父节点是黑色的时候,并不会影响红黑树的平衡,直接插入即可,无需做自平衡。

情景4:插入节点的父节点为红色

情景4.1 叔叔节点存在并且为红色节点

根据红黑树性质可知,红色节点不能相连,那么爷爷节点肯定为黑色节点。
那么解决方案就是:将父亲节点和叔叔节点改为黑色,爷爷节点改为红色,但爷爷节点可能是根节点,也可能爷爷的父亲也是红色节点,所以还需要对爷爷节点进行处理,直到平衡为止。

情景4.2 叔叔节点存在而且是黑色节点

为什么叔叔节点会是黑色节点呢,一开始我也想了很久。后来找到了下面这个样例,才觉得是可能的。
首先看下图左边的红黑树,此时插入了一个新节点x,经过第一轮变换后,到了中间的红黑树,此时x节点变成了之前的爷爷节点,现在应该对新的x节点进行转换,它发现它的叔叔是黑色节点,那么首先将爷爷节点进行右旋,xp 成为了新的根节点,再将xp改为黑色节点,xpp改为红色节点,就完成了第二次转换,如下图右边的红黑树。

情景4.3 叔叔节点不存在
4.3.1 插入节点是父亲节点的左孩子

直接将xpp进行右旋,并且将xp改为黑色,xpp改为红色。

4.3.2 插入节点是父亲节点的右孩子

如果插入节点是父亲节点的右孩子,那么首先需要将xp左旋,然后就跟4.3.1案例一样了,然后直接将xpp右旋。

我之前有个疑问,为啥不可以直接将xpp右旋呢,比如下面,但这样是不对的,因为这样违反了规则5:从任一结点到其每个叶子的所有路径都包含相同数目的黑色节点。所以需要先将xp左旋,然后在将xpp右旋,这样就满足了规则5。

以上展示的是父节点是爷爷的左孩子,关于父节点是爷爷的右孩子同理,就不在赘述了。

插入源代码

static <K, V> TreeNode<K, V> balanceInsertion(TreeNode<K, V> root,
                                              TreeNode<K, V> x) {
    // x 为插入节点,将其颜色设置为null
    x.red = true;
    TreeNode<K, V> xp, xpp, xppl, xppr;
    while (true) {
        xp = x.parent;
        // 1.如果插入节点的父亲为null,则它是根节点
        // 并将其设置成黑色
        if (xp == null) {
            x.red = false;
            return x;
            // 如果父亲节点为黑色,那么插入一个红色节点不会影响平衡,直接返回
        } else if (!xp.red) {
            return root;
        } else {
            // TODO: 如果父亲节点是根节点的话,那不应该是黑色嘛
            xpp = xp.parent;
            if (xpp == null) {
                return root;
            }
        }
        // 此时父亲肯定是红色
        xppl = xpp.left;
        xppr = xpp.right;
        if (xp == xppl) {
            if (xppr != null && xppr.red) {
                xppr.red = false;
                xp.red = false;
                xpp.red = true;
                // 将爷爷节点设置为插入节点,因为爷爷节点变成了红色,
                // 可能会破坏平衡,所以需要重新走一遍平衡
                x = xpp;
            } else {
                // 到这里,证明它的叔叔节点为空或者为黑色

                // 如果插入节点是父亲节点的右孩子
                if (x == xp.right) {
                    // 先将父节点左旋
                    x = xp;
                    root = rotateLeft(root, x);
                    xp = x.parent;
                    xpp = xp == null ? null : xp.parent;
                }
                // 如果有父节点
                if (xp != null) {
                    // 父节点设置成黑色
                    xp.red = false;
                    if (xpp != null) {
                        // 爷爷节点设置成红色
                        xpp.red = true;
                        // 将爷爷节点右旋
                        root = rotateRight(root, xpp);
                    }
                }
            }
        } else {
            if (xppl != null && xppl.red) {
                xppl.red = false;
                xp.red = false;
                xpp.red = true;
                x = xpp;
            } else {
                if (x == xp.left) {
                    x = xp;
                    root = rotateRight(root, x);
                    xp = x.parent;
                    xpp = xp == null ? null : xp.parent;
                }
                if (xp != null) {
                    xp.red = false;
                    if (xpp != null) {
                        xpp.red = true;
                        root = rotateLeft(root, xpp);
                    }
                }
            }
        }
    }
}

static <K, V> TreeNode<K, V> rotateLeft(TreeNode<K, V> root, TreeNode<K, V> p) {
    if (p == null || p.right == null)
        return root;
    TreeNode<K, V> pp = p.parent; // 父节点
    TreeNode<K, V> pr = p.right; // 右孩子
    TreeNode<K, V> prl = pr.left;// 右孩子的左孩子
    p.right = prl;
    if (prl != null) {
        prl.parent = p;
    }
    pr.parent = pp;
    if (pp == null) {
        root = pr;
        root.red = false;
    } else if (p == pp.left) {
        pp.left = pr;
    } else {
        pp.right = pr;
    }
    pr.left = p;
    p.parent = pr;
    return root;
}

static <K, V> TreeNode<K, V> rotateRight(TreeNode<K, V> root, TreeNode<K, V> p) {
    if (p == null || p.left == null)
        return root;

    TreeNode<K, V> pp = p.parent;
    TreeNode<K, V> pl = p.left;
    TreeNode<K, V> plr = pl.left;
    p.left = plr;
    if (plr != null) {
        plr.parent = p;
    }
    // 更新旋转节点的父节点
    pl.parent = pp;
    if (pp == null) {
        root = pl;
        root.red = false;
    } else if (p == pp.left) {
        pp.left = pl;
    } else {
        pp.right = pl;
    }
    pl.right = p;
    p.parent = pl;
    return root;
}

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

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

相关文章

xss 一些例子

目录 XSS 1.Ma Spaghet!​编辑 2.Jefff​编辑 3.Ugandan Knuckles​编辑 4.Ricardo Milos​编辑 5.Ah Thats Hawt​编辑 6.Ligma​编辑 7.Mafia​编辑 简单解法就是换一个函数 作者得原意解法 8.Ok, Boomer​编辑 XSS 1.Ma Spaghet! 这里接收了一个somebody参数&…

Chain of Thought (CoT) 系列论文:大模型思维链,提升 LLM 的推理能力

文章目录 1. COT&#xff1a;Chain of Thought1. 研究背景2. CoT的原理3. CoT Prompt 1. COT&#xff1a;Chain of Thought COT 是 2022.01 由 google 提出的针对提升 LLM 的推理能力的 Prompt Engineering 方法。 paper&#xff1a; Chain-of-Thought Prompting Elicits Re…

一器多能,数据文件处理的瑞士军刀 — dasel

Dasel&#xff1a;简化数据操作&#xff0c;提升开发效率。- 精选真开源&#xff0c;释放新价值。 概览 dasel是一款专为开发者设计的高效数据文件操作工具&#xff0c;它允许用户通过统一的接口对JSON、TOML、YAML、XML和CSV等格式的文件进行数据选择、插入和删除操作。这款工…

Kafka基本概念及消费流程

Kafka是消息中间件的一种&#xff0c;相较于其他消息中间件&#xff0c;其以极高的吞吐量闻名&#xff0c;常用于构建实时数据管道和流应用&#xff0c;能够处理高吞吐量的数据流。以下是Kafka中的重要概念&#xff1a; 1. 生产者 生产者是向Kafka主题发送消息的客户端。生产…

登录 k8s-Dashboard 显示 Your connection is not private

文章目录 一、背景二、解决方案 一、背景 部署好 kubernetes-Dashboard 后使用 master节点的 ipport 登录 Dashboard 显示 Your connection is not private 无论是 Edge 还是 Google Chrome 都是这样的情况 二、解决方案 点击网页空白处&#xff0c;英文输入法输入&#xf…

论文解读:LONGWRITER: UNLEASHING 10,000+ WORD GENERATION FROM LONG CONTEXT LLMS

摘要 现象&#xff1a;当前的大预言模型可以接受超过100,000个tokens的输入&#xff0c;但是却难以生成超过2000个token的输出。 原因&#xff1a;监督微调过程(SFT)中看到的样本没有足够长的样本。 解决方法&#xff1a; Agent Write&#xff0c;可以将长任务分解为子任务&a…

为什么MCU I2C波形中会出现的脉冲毛刺?

在I2C的波形中&#xff0c;经常会发现有这样的脉冲毛刺&#xff0c;会被认为是干扰或者器件不正常。 看到这个波形时&#xff0c;可以先数一下出现在第几个clock的位置&#xff0c;如果出现在第9个clock的低电平期间&#xff0c;就不是干扰或者器件异常导致。 在I2C的协议中&a…

Java并发类的主要API方法-CountDownLatch和CyclicBarrier

1.概念介绍 CountDownLatch 是一个计数器&#xff0c;计数器的初始值由创建它时指定。每次调用 countDown() 方法时&#xff0c;计数器会减1&#xff0c;直到计数器值变为0时&#xff0c;所有调用 await() 的线程都会被唤醒继续执行。 CyclicBarrier 是 Java 中另一个常用的同…

基于CDIO概念的人工智能物联网系统开发与实施的人才培养研究

目录 1. 引言&#xff08;Introduction&#xff09; 2. AIoT技术及其培训特点&#xff08;The Characteristics of AIOT and Its Training&#xff09; 3. 基于CDIO概念的AIoT课程改革&#xff08;CDIO Concept-based Reform of AIOT Course&#xff09; 4. AIoT课程内容安…

SweetAlert2

1. SweetAlert2 SweetAlert2是一个基于JavaScript的库, 用于在网页上替换标准的警告框(alert), 确认框(confirm)和提示框(prompt), 并提供更加美观和用户友好的界面.需要在项目中引入SweetAlert2, 可以通过CDN链接或者将库文件下载到你的项目中来实现这一点. 通过CDN引入:<…

C++:stack类(vector和list优缺点、deque)

目录 前言 数据结构 deque vector和list的优缺点 push pop top size empty 完整代码 前言 stack类就是数据结构中的栈 C数据结构&#xff1a;栈-CSDN博客 stack类所拥有的函数相比与string、vector和list类都少很多&#xff0c;这是因为栈这个数据结构是后进先出的…

SPRING09_ Bean后置处理器创建过程、SmartInstantiationAwareBeanPostProcessor预测方法调用

文章目录 ①. Bean后置处理器创建过程②. SmartInstantiationAwareBeanPostProcessor预测方法调用 ①. Bean后置处理器创建过程 ①. 坏境准备,在BeanPostProcessor的无参构造器、postProcessBeforeInitialization以及postProcessAfterInitialization打上断点.以xml的方式启动容…

秋招突击——8/15——新作{最大子数组和、合并区间、转轮数组、除自身以外的数组的乘积}

文章目录 引言新作最大子数组和个人实现参考实现 合并区间个人实现短板补充——自定义排序标准 参考实现 转轮数组最终实现 除自身以外数组的乘积个人实现 总结 引言 以前刷题的方式方法有问题&#xff0c;花太多时间了&#xff0c;应该先过一遍&#xff0c;然后再针对特定的题…

第一百九十四节 Java集合教程 - Java优先级队列

Java集合教程 - Java优先级队列 优先级队列是其中每个元素具有相关联的优先级的队列。具有最高优先级的元素将从队列中删除。 PriorityQueue 是一个实现类对于Java Collection Framework中的无界优先级队列。 我们可以使用在每个元素中实现的 Comparable 接口作为其优先事项。…

C# OnnxRuntime YoloV5 Demo

目录 效果 模型信息 项目 代码 Form1.cs YoloV5.cs 下载 效果 模型信息 Model Properties ------------------------- --------------------------------------------------------------- Inputs ------------------------- name&#xff1a;images tensor&#xff1a…

机器学习/人工智能中的学习证明

一、说明 在进行任何数学发展之前&#xff0c;我们必须首先了解学习的基础以及它如何与错误的概念密切相关。关于代价函数&#xff0c;它的工作原理是梯度下降原理。本文将回顾梯度下降原理。 二、假想的厨师 想象一下&#xff0c;在任何一天&#xff0c;你决定复制你在一家著名…

8.13 Day19 Windows服务器(Windows service 2008 R2)上域的搭建 (1)

域服务器&#xff08;DC&#xff09;&#xff1a;安装了活动目录服务的服务器就称为DC。 将三台设备配置在同一网络中&#xff0c;此处将外部网络隔离开&#xff0c;只将他们放在局域网中 服务端网络配置&#xff0c;此时与外部网络彻底隔绝开&#xff0c;且已无法和主机通信&…

XSS game复现(DOM型)

目录 1.Ma Spaghet! 2.Jefff 3.Ugandan Knuckles 4.Ricardo Milos 5.Ah Thats Hawt 6.Ligma 7.Mafia 8.Ok, Boomer 1.Ma Spaghet! 通过简单的尝试发现传递参数可以直接进入h2标签 接下来我们尝试传入一个alert(1) 可以看到并没有触发。原因是在innerHTML中官方禁用了sc…

二进制安装php

下载php二进制包&#xff1a; 官网地址&#xff1a;https://www.php.net/releases/ PHP: Releaseshttps://www.php.net/releases/在里边可以选择自己要下载的包进行下载&#xff1b; 下载完成后进行解压&#xff1a; tar xvzf php-7.3.12.tar.gz 解压后 进入目录进行预编…

xss案例

首先进入XSS Game - Learning XSS Made Simple! | Created by PwnFunction打开环境 Ma Spaghet 在script里面给使用get传参给somdbody传一个值&#xff0c;若没有传值&#xff0c;默认传SomebodyToucha Ma Spaghet!,赋值给spaghet,放在h2标签中&#xff0c;spaghet后会有一个in…