平衡搜索树——红黑树小记

news2024/11/24 6:56:53

文章目录

  • 红黑树
    • 定义
    • 规则
    • 操作规则
    • 平衡调整规则
      • 规则
    • 代码
      • 插入
      • 平衡调整代码
      • 左旋、右旋

红黑树

定义

红黑树是一种 “平衡” 二叉 搜索树
“平衡”: 相比较于AVL树来说,是一种弱平衡
在红黑树中,任意从根到叶子的路径中,LEN(最长的路径)<= 2*LEN(最短的路径)

规则

  1. 红黑树中的结点:或红或黑
  2. 红黑树的根一定是黑色的
  3. 红黑树的“叶子结点”一定是黑色的。(此处的叶子: node == null)
  4. 红黑树中,任意路径(从根到叶子),红色不能和红色相邻
  5. 红黑树中,任意路径(从根到叶子),上面黑色结点的数量必须一样多

操作规则

查找操作:同普通的搜索树规则
插入操作:碰瓷“红色不能和红色相邻”这条规则——新插入的结点,一定是红色的
1.按照普通搜索树方式插入
2.判断是否破坏了红色不能红色相邻的规则,如果破坏了,则进行平衡调整操作
3.将根置为黑色

平衡调整规则

假设:新插入的结点(node)破坏了“红色不能相邻”的规则,且:

node的父节点: parent
node 的祖父结点: grandpa
node的叔叔结点: uncle(假如存在)

其中,node既可能是新插入的结点,也可能是不断向根回溯过程中的"grandpa"

根据红黑树定义及规则可知,以下成立:

  • parent一定存在,且一定是红色
  • grandpa一定存在,且一定是黑色

规则

1. uncle存在 && uncle的颜色是红色
在这里插入图片描述

步骤:

  1. parent的颜色=黑色 uncle的颜色=黑色
  2. grandpa的颜色=红色
  3. 把 grandpa视为node,重新循环进行再平衡的过程,直到回溯到根上为止

在这里插入图片描述
2.uncle 不存在或者(uncle存在&& uncle是黑色的)

(1) parent和grandpa的关系node和parent的关系不一致
在这里插入图片描述
步骤
在这里插入图片描述
如果原来关系一致,不需要做此步骤。

3.关系一致后处理
在这里插入图片描述

优点

不改变路径上的黑色数量同时解决了parent和node红色相邻的问题

代码

插入

 /**
     * 保存红黑树的根结点
     */
    public RBTreeNode root = null;
    /**
     * 保存红黑树中的结点个数
     */
    public int size = 0;

    /**
     * 向红黑树中插入新的关键字
     * @param key 关键字
     * @return 是否插入成功
     * true: 插入成功
     * false: 插入失败(key 出现重复)
     */
    public boolean insert(long key) {
        // 1. 按照普通搜索树的方式进行插入
        if (root == null) {
            root = new RBTreeNode(key, null, BLACK);
            size++;
            return true;
        }

        RBTreeNode parent = null;
        RBTreeNode current = root;

        while (current != null) {
            if (key == current.key) {
                return false;
            } else if (key < current.key) {
                parent = current;
                current = current.left;
            } else {
                parent = current;
                current = current.right;
            }
        }

        /**
         * 根据插入规则,每次新插入的结点,一定是红色的
         */
        RBTreeNode node = new RBTreeNode(key, parent, RED);
        if (key < parent.key) {
            parent.left = node;
        } else {
            parent.right = node;
        }
        size++;

        /**
         * 进行红黑树规则的判断 + 平衡调整过程
         */
        adjustBalance(node);

        return true;
    }

平衡调整代码

private void adjustBalance(RBTreeNode node) {
        while (true) {
            RBTreeNode parent = node.parent;
            if (parent == null) {
                break;
            }

            if (parent.color == BLACK) {
                break;
            }

            /**
             * 一定破坏了"红色不能相邻"的规则
             */
            RBTreeNode grandpa = parent.parent;

            // 找到叔叔结点
            if (parent == grandpa.left) {
                RBTreeNode uncle = grandpa.right;
                if (uncle != null && uncle.color == RED) {
                    /**
                     * 情况1:叔叔存在 并且 叔叔的颜色是红色
                     * 步骤:
                     * 1. 叔叔和父亲的颜色改成黑色
                     * 2. 祖父的颜色改成红色
                     * 3. 把祖父视为 node,再去判断是否违反规则了
                     */
                    parent.color = uncle.color = BLACK;
                    grandpa.color = RED;
                    node = grandpa;
                    continue;
                } else {
                    // 判断 grandpa <-> parent 和 parent <-> node 的关系是否不一致
                    // 已知 parent 是 grandpa 的左边
                    if (node == parent.right) {
                        leftRotate(parent);

                        //swap(parent, node);
                        parent = node;
                    }

                    // 接下来统一处理关系一致的情况
                    rightRotate(grandpa);
                    grandpa.color = RED;
                    parent.color = BLACK;
                    break;
                }
            } else {
                RBTreeNode uncle = grandpa.left;
                if (uncle != null && uncle.color == RED) {
                    /**
                     * 情况1:叔叔存在 并且 叔叔的颜色是红色
                     * 步骤:
                     * 1. 叔叔和父亲的颜色改成黑色
                     * 2. 祖父的颜色改成红色
                     * 3. 把祖父视为 node,再去判断是否违反规则了
                     */
                    parent.color = uncle.color = BLACK;
                    grandpa.color = RED;
                    node = grandpa;
                    continue;
                } else {
                    // 判断 grandpa <-> parent 和 parent <-> node 的关系是否不一致
                    // 已知 parent 是 grandpa 的右边
                    if (node == parent.left) {
                        rightRotate(parent);

                        //swap(parent, node);
                        parent = node;
                    }

                    // 接下来统一处理关系一致的情况
                    leftRotate(grandpa);
                    grandpa.color = RED;
                    parent.color = BLACK;
                    break;
                }
            }
        }

        /**
         * 无论之前是什么情况,统一把根改成黑色
         * 走到此处时,root 一定不是 null
         */
        root.color = BLACK;
    }

左旋、右旋

与AVL树左旋右旋原理一致。可参考上篇博客中左旋调整平衡部分。
在这里插入图片描述

private void leftRotate(RBTreeNode m) {
        // m 代表图中的 b 结点
        // parent 代表 b 结点可能存在的父亲
        RBTreeNode parent = m.parent;
        // right 代表图中的 a 结点
        RBTreeNode right = m.right;
        // leftOfRight 代表图中的可能存在的乙子树的根结点
        RBTreeNode leftOfRight = right.left;
        /*
        其中: m != null && right != null
        但是: parent 不保证 !null, leftOfRight 不保证 !null
         */

        right.parent = parent;  // 蓝色线的关系
        // 黑色线的关系
        if (parent == null) {
            // m 是 root
            root = right;
        } else {
            if (m == parent.left) {
                parent.left = right;
            } else {
                parent.right = right;
            }
        }

        right.left = m; // 黑色线的关系
        m.parent = right;   // 蓝色线的关系

        m.right = leftOfRight;
        if (leftOfRight != null) {
            leftOfRight.parent = m;
        }
    }

    private void rightRotate(RBTreeNode m) {
        RBTreeNode parent = m.parent;
        RBTreeNode left = m.left;
        RBTreeNode rightOfLeft = left.right;

        left.parent = parent;
        if (parent == null) {
            root = left;
        } else {
            if (m == parent.left) {
                parent.left = left;
            } else {
                parent.right = left;
            }
        }

        left.right = m;
        m.parent = left;

        m.left = rightOfLeft;
        if (rightOfLeft != null) {
            rightOfLeft.parent = m;
        }
    }

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

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

相关文章

Keras深度学习实战(39)——音乐音频分类

Keras深度学习实战&#xff08;39&#xff09;——音乐音频分类0. 前言1. 数据集与模型分析1.1 数据集分析1.2 模型分析2. 歌曲流派分类模型2.1 数据加载与预处理2.2 模型构建与训练3. 聚类分析小结系列链接0. 前言 音乐音频分类技术能够基于音乐内容为音乐添加类别标签,在音乐…

爬虫基本原理

爬虫基本原理 网络爬虫的本质 爬虫是模仿用户在浏览器或者某个应用上的操作&#xff0c;把操作的过程实现自动化的程序 数据的传输是由客户端和服务器来进行交互的&#xff0c; 他们进行交互的层是传输层&#xff0c;遵守TIP/IP协议 我们在查询一个网址之后发生了四个步骤 …

spring复习05,spring整合mybatis,声明式事务

spring复习05,spring整合mybatis,声明式事务spring整合mybatis1. 在pom.xml中导入依赖2. 创建实体类3. 创建Mapper接口4. 配置mybatis核心配置文件5. 编写映射文件Mapper.xml6. 编写数据源配置7. sqlSessionFactory8. sqlSessionTemplate9. 需要给接口加实现类10. 将实现类注入…

JavaScript获取DOM元素相关信息和属性

getBoundingClientRect 获取到元素盒模型的一些信息,得到的结果是没有单位的,不包含滚动条的距离,不包含margin&#xff0c;包含border和padding width 宽度&#xff08;包含边框&#xff09; height 高度&#xff08;包含边框&#xff09; left 从元素最左边到可视区最左边距…

Ansible 企业级自动化运维实战

一、Ansible 简介 如果Ansible不采用0mq(ZeroMQ),在操作1000个以下的节点性能还可以,如果操作1000个以上的节点,性能就很差。 目前来说Ansible支持local,ssh,0mq,Ansible用ssh来管理被管理主机是最常见的方法。 saltstack简称salt,默认采用0mq(ZeroMQ),支持数万…

[附源码]Python计算机毕业设计Django大学生心理健康测评系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

【Java】并发模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pt4IAyjj-1669730661631)(https://gitee.com/github-25970295/blogpictureV2/raw/master/java-concurrent-overview-1.png)] 1. 并发问题的根源 可见性&#xff1a;一个线程对共享变量的修改&#xf…

GIT error: Committing is not possible because you have unmerged files.

翻译&#xff1a;错误:无法提交&#xff0c;因为您有未合并的文件。 git 上传文件报错原因&#xff1a; 远程仓库的文件与在本地被删除了&#xff0c;本地删除文件后重新创建一个相同文件名的文件 远程仓库&#xff1a; 解决方法&#xff1a; 代码重新提交&#xff0c;重新合…

xv6---Lab2: system calls

目录 参考资料&#xff1a; 2.1 抽象物理资源 2.2 特权模式与系统调用 2.3 内核的组织 2.5 进程概览 2.6 Code: 启动xv6&#xff0c;第一个进程和系统调用 4.2 Trap from user space System call tracing 关于syscall函数的代码 每个syscall是由usys.pl自动生成为us…

SAP 异常现象之同一个IDoc可以被POST两次触发2张不同的物料凭证

SAP 异常现象之同一个IDoc可以被POST两次触发2张不同的物料凭证 玩过SAP IDoc的童鞋都知道&#xff0c;一个IDoc正常情况下是只能被POST一次的&#xff0c;不可以POST两次的。 比如如下的IDoc 0000000205423126已经被POST了&#xff0c;其状态为53&#xff0c; Material Docume…

2-FreeRTOS编码标准、风格指南

1- 编码标准 FreeRTOS源文件(对所有端口通用&#xff0c;但对端口层不通用)符合MISRA编码标准指南。使用pc-lint和链接lint配置文件检查遵从性。由于标准有很多页长&#xff0c;并且可以从MISRA处以非常低的费用购买&#xff0c;所以我们在这里没有复制所有的规则。 就是下面这…

时间序列:时间序列模型---白噪声

本文是Quantitative Methods and Analysis: Pairs Trading此书的读书笔记。 白噪声&#xff08;white noise)是最简单的随机时间序列&#xff08;stochastic time series)。 在每一时刻&#xff0c;从一个正态分布中抽取一个值从而形成白噪声时间序列。并且&#xff0c;这个正…

1. 关于pytorch中的数据操作的广播机制

在某些情况下&#xff0c;即使形状不同&#xff0c;我们仍然可以通过调用 广播机制&#xff08;broadcasting mechanism&#xff09;来执行按元素操作。 这种机制的工作方式如下&#xff1a;首先&#xff0c;通过适当复制元素来扩展一个或两个数组&#xff0c; 以便在转换之后&…

第七章 贝叶斯分类器(下)

7.5 贝叶斯网 贝叶斯网亦称“信念网”&#xff0c;它借助有向无环图&#xff08;DAG&#xff09;来刻画属性之间的依赖关系&#xff0c;并使用条件概率表&#xff08;CPT&#xff09;来描述属性的联合概率分布。 具体来说&#xff0c;一个贝叶斯网B由结果G和参数Θ两部分构成&…

安全漏洞分类之CNNVD漏洞分类指南

适用范围说明 凡是被国家信息安全漏洞库&#xff08;CNNVD&#xff09;收录的漏洞&#xff0c;均适用此分类规范&#xff0c;包括采集的公开漏洞以及收录的未公开漏洞&#xff0c;通用型漏洞及事件型漏洞。 漏洞类型 CNNVD将信息安全漏洞划分为26种类型&#xff0c;分别是&…

基于Tree-LSTM网络语义表示模型

TC&#xff1b;DR 目前的LSTM仅能对序列信息进行建模&#xff0c; 但是自然语言中通常由词组成的短语形成了句法依存的语义树。为了学习到树结构的语义信息。论文中提出了两种Tree-LSTM模型。Child-Sum、Tree-LSTM、和N-ary Tree LSTMs。实验部分的Tree-LSTM、对比多种LSTMs的…

09【MyBatis多表关联查询】

文章目录三、MyBatis多表关联查询3.1 表的关系3.2 一对一查询3.2.1 搭建环境3.2.2 需求分析3.2.3 dao接口3.2.3 mapper.xml3.2.4 测试3.2.5 配置MyBatis一对一关系1&#xff09;传统映射&#xff1a;2&#xff09;使用association标签映射3.3 一对多查询3.3.1 需求分析3.3.2 da…

Codeforces Round #799 (Div. 4) H. Gambling

翻译&#xff1a; 玛丽安在赌场。赌场里的游戏是这样的。 在每一轮之前&#xff0c;玩家在1到109之间选择一个数字。在那之后&#xff0c;一个有109个面的骰子被滚动&#xff0c;这样就会出现1到109之间的随机数。如果玩家猜对了数字&#xff0c;他们的钱就会翻倍&#xff0c…

C++原子操作和互斥锁性能(速度)对比

先说结论&#xff1a;原子操作性能&#xff08;速度&#xff09;强于互斥锁&#xff0c;下面用例子进行说明。 编写测试demo&#xff0c;开启两个线程&#xff0c;对全局变量n分别进行自增、自减操作&#xff0c;计算执行时间。 首先看没有用任何手段进行互斥的情况&#xff0c…

Springboot生成Word/EXECL/PPTX文档

目录 一、概述 二、使用介绍 第一种Poi-tl&#xff1a; 1、介绍 2、功能 第二种Poi: 什么是POI 二进制分布 源码分发 一、概述 Word模板引擎&#xff1a;使用Word模板和数据生成对应的Word文档。 方案移植性功能性易用性 Poi-tl Java跨平台 Word模板引擎&#…