数据结构(7)-红黑树

红黑树(Red Black Tree,RBTree) 一、概念 红黑树是一种自平衡二叉查找树(二叉排序树)。与平衡二叉树(avl树)不同的是,红黑树是弱平衡二叉树,即它的左右子树高度差有可能大于1。 二、5大性质 每个节点要么是黑色, 要么是红色。 根节点是黑色。 每个叶节点(Nil,空值)都是黑色。 每个红色节点的两个子节点一定都是黑色。 任意一个节点到每个叶子节点的路径都包含相同数量的黑节点。 三、推论(或规律) 左子树和右子树的黑节点的层数是相等的。因此我们称红黑树的这种平衡为黑色完美平衡。(由性质5推出) 如果一个节点存在黑子节点,那么该节点肯定 阅读详情


一、什么是红黑树

红黑树Red-Black Tree(简称R-B Tree)是一种二叉树类的数据结构,红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。
在这里插入图片描述

二、红黑树的特性

1、每个节点或者是黑色,或者是红色。
2、根节点是黑色。
3、每个叶子节点(或NULL节点)是黑色。
4、如果一个节点是红色的,则它的子节点必须是黑色的。
5、从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

红黑树通过上述的5个特性可以保持二叉搜索树的相对平衡,从而达到更高效地查找、删除的目的。

三、红黑树的Java实现

1、基本定义
public class RBTree<T extends Comparable<T>> {

    private RBTNode<T> mRoot;    // 根结点

    private static final boolean RED   = false;
    private static final boolean BLACK = true;

    public class RBTNode<T extends Comparable<T>> {
        boolean color;        // 颜色
        T key;                // 关键字(键值)
        RBTNode<T> left;    // 左孩子
        RBTNode<T> right;    // 右孩子
        RBTNode<T> parent;    // 父结点

        public RBTNode(T key, boolean color, RBTNode<T> parent, RBTNode<T> left, RBTNode<T> right) {
            this.key = key;
            this.color = color;
            this.parent = parent;
            this.left = left;
            this.right = right;
        }

    }

    ...
}
2、左旋

在这里插入图片描述

private void leftRotate(RBTNode<T> x) {
    // 设置x的右孩子为y
    RBTNode<T> y = x.right;

    // 将 “y的左孩子” 设为 “x的右孩子”;
    // 如果y的左孩子非空,将 “x” 设为 “y的左孩子的父亲”
    x.right = y.left;
    if (y.left != null)
        y.left.parent = x;

    // 将 “x的父亲” 设为 “y的父亲”
    y.parent = x.parent;

    if (x.parent == null) {
        this.mRoot = y;            // 如果 “x的父亲” 是空节点,则将y设为根节点
    } else {
        if (x.parent.left == x)
            x.parent.left = y;    // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
        else
            x.parent.right = y;    // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
    }
    
    // 将 “x” 设为 “y的左孩子”
    y.left = x;
    // 将 “x的父节点” 设为 “y”
    x.parent = y;
}
3、右旋

在这里插入图片描述

private void rightRotate(RBTNode<T> y) {
    // 设置x是当前节点的左孩子。
    RBTNode<T> x = y.left;

    // 将 “x的右孩子” 设为 “y的左孩子”;
    // 如果"x的右孩子"不为空的话,将 “y” 设为 “x的右孩子的父亲”
    y.left = x.right;
    if (x.right != null)
        x.right.parent = y;

    // 将 “y的父亲” 设为 “x的父亲”
    x.parent = y.parent;

    if (y.parent == null) {
        this.mRoot = x;            // 如果 “y的父亲” 是空节点,则将x设为根节点
    } else {
        if (y == y.parent.right)
            y.parent.right = x;    // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子”
        else
            y.parent.left = x;    // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子”
    }

    // 将 “y” 设为 “x的右孩子”
    x.right = y;

    // 将 “y的父节点” 设为 “x”
    y.parent = x;
}
4、添加

将一个节点插入到红黑树中,需要执行哪些步骤呢?首先,将红黑树当作一颗二叉查找树,将节点插入;然后,将节点着色为红色;最后,通过"旋转和重新着色"等一系列操作来修正该树,使之重新成为一颗红黑树。详细描述如下:

第一步: 将红黑树当作一颗二叉查找树,将节点插入。
红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。此外,无论是左旋还是右旋,若旋转之前这棵树是二叉查找树,旋转之后它一定还是二叉查找树。这也就意味着,任何的旋转和重新着色操作,都不会改变它仍然是一颗二叉查找树的事实。
好吧?那接下来,我们就来想方设法的旋转以及重新着色,使这颗树重新成为红黑树!

第二步:将插入的节点着色为"红色"。
为什么着色成红色,而不是黑色呢?为什么呢?在回答之前,我们需要重新温习一下红黑树的特性:
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!]
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
将插入的节点着色为红色,不会违背"特性(5)"!少违背一条特性,就意味着我们需要处理的情况越少。接下来,就要努力的让这棵树满足其它性质即可;满足了的话,它就又是一颗红黑树了。o(∩∩)o…哈哈

第三步: 通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。
第二步中,将插入节点着色为"红色"之后,不会违背"特性(5)"。那它到底会违背哪些特性呢?
对于"特性(1)",显然不会违背了。因为我们已经将它涂成红色了。
对于"特性(2)",显然也不会违背。在第一步中,我们是将红黑树当作二叉查找树,然后执行的插入操作。而根据二叉查找数的特点,插入操作不会改变根节点。所以,根节点仍然是黑色。
对于"特性(3)",显然不会违背了。这里的叶子节点是指的空叶子节点,插入非空节点并不会对它们造成影响。
对于"特性(4)",是有可能违背的!
那接下来,想办法使之"满足特性(4)",就可以将树重新构造成红黑树了。

private void insert(RBTNode<T> node) {
    int cmp;
    RBTNode<T> y = null;
    RBTNode<T> x = this.mRoot;

    // 1. 将红黑树当作一颗二叉查找树,将节点添加到二叉查找树中。
    while (x != null) {
        y = x;
        cmp = node.key.compareTo(x.key);
        if (cmp < 0)
            x = x.left;
        else
            x = x.right;
    }

    node.parent = y;
    if (y!=null) {
        cmp = node.key.compareTo(y.key);
        if (cmp < 0)
            y.left = node;
        else
            y.right = node;
    } else {
        this.mRoot = node;
    }

    // 2. 设置节点的颜色为红色
    node.color = RED;

    // 3. 将它重新修正为一颗二叉查找树
    insertFixUp(node);
}

/* 
 * 新建结点(key),并将其插入到红黑树中
 *
 * 参数说明:
 *     key 插入结点的键值
 */
public void insert(T key) {
    RBTNode<T> node=new RBTNode<T>(key,BLACK,null,null,null);

    // 如果新建结点失败,则返回。
    if (node != null)
        insert(node);
}

内部接口 – insert(node)的作用是将"node"节点插入到红黑树中。
外部接口 – insert(key)的作用是将"key"添加到红黑树中。

添加修正操作的实现代码(Java语言)

private void insertFixUp(RBTNode<T> node) {
    RBTNode<T> parent, gparent;

    // 若“父节点存在,并且父节点的颜色是红色”
    while (((parent = parentOf(node))!=null) && isRed(parent)) {
        gparent = parentOf(parent);

        //若“父节点”是“祖父节点的左孩子”
        if (parent == gparent.left) {
            // Case 1条件:叔叔节点是红色
            RBTNode<T> uncle = gparent.right;
            if ((uncle!=null) && isRed(uncle)) {
                setBlack(uncle);
                setBlack(parent);
                setRed(gparent);
                node = gparent;
                continue;
            }

            // Case 2条件:叔叔是黑色,且当前节点是右孩子
            if (parent.right == node) {
                RBTNode<T> tmp;
                leftRotate(parent);
                tmp = parent;
                parent = node;
                node = tmp;
            }

            // Case 3条件:叔叔是黑色,且当前节点是左孩子。
            setBlack(parent);
            setRed(gparent);
            rightRotate(gparent);
        } else {    //若“z的父节点”是“z的祖父节点的右孩子”
            // Case 1条件:叔叔节点是红色
            RBTNode<T> uncle = gparent.left;
            if ((uncle!=null) && isRed(uncle)) {
                setBlack(uncle);
                setBlack(parent);
                setRed(gparent);
                node = gparent;
                continue;
            }

            // Case 2条件:叔叔是黑色,且当前节点是左孩子
            if (parent.left == node) {
                RBTNode<T> tmp;
                rightRotate(parent);
                tmp = parent;
                parent = node;
                node = tmp;
            }

            // Case 3条件:叔叔是黑色,且当前节点是右孩子。
            setBlack(parent);
            setRed(gparent);
            leftRotate(gparent);
        }
    }

    // 将根节点设为黑色
    setBlack(this.mRoot);
}
5、删除操作

红黑树的删除操作十分复杂,一般不会直接使用删除操作,而是使用伪删除,具体参考 java面试-彻底搞懂红黑树 https://blog.youkuaiyun.com/qq_34173549/article/details/79636764

四、参考资料

1、Java数据结构和算法(十一)——红黑树 https://www.cnblogs.com/ysocean/p/8004211.html
2、红黑树(五)之 Java的实现 https://www.cnblogs.com/skywang12345/p/3624343.html
3、java面试-彻底搞懂红黑树 https://blog.youkuaiyun.com/qq_34173549/article/details/79636764
4、红黑树 https://baike.baidu.com/item/红黑树/2413209?fr=aladdin

红黑树简介及左旋、右旋、变色 红黑树简介及红黑树的左旋、右旋、变色 阅读详情

相关推荐

红黑树的特点和使用场景

红黑树(Red-Black Tree)是一种,它在插入、删除和查找操作时保持了树的平衡性,从而保证了这些操作的时间复杂度为 O(log n)。红黑树在实际应用中广泛使用,例如在操作系统的内核中用于管理进程调度、文件系统、以及网络 I/O(如epoll机制中)。

m0_48022770的博客 1714

【数据结构】史上最好理解的红黑树讲解,让你彻底搞懂红黑树

狂肝半个月的学习笔记,最通俗易懂的红黑树讲解。带你快速掌握红黑树~

小七的博客 29万+

【Unity游戏开发】SDK接入与集成——小白入门篇

一、简介   通常一款游戏开发到后期,一般都会涉及到第三方SDK的接入与集成,对于不熟悉SDK接入的同学来说,接SDK每次都是云里雾里,而熟悉SDK接入的同学又觉得不断地重复做接入SDK工作这样没有成就感,太尼玛无聊了(Android渠道一弄就十几个,直接吐血)。其实通常情况下接入SDK都是很简单的一个过程,本系列博客就让马三和大家从小白开始,从零基础开始学习如何接入SDK以及一些常见的SDK的...

weixin_34195142的博客 5764

红黑树介绍

红黑树一、红黑树的概念红黑树的性质红黑树节点的定义红黑树结构二、使用步骤1.引入库2.读入数据总结 一、红黑树的概念 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。 红黑树的性质 每个结点不是红色就是黑色 根节点是黑色的 如果一个节点是红色的,则它的两个孩子结点是黑色的 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数

mz474920631的博客 4万+

第三篇:学习InternVL2.0(探讨代码之训练模块版)

InternVL2的训练模块

qq_69051120的博客 1245

开源模型应用落地-qwen模型小试-Qwen3-8B-快速体验(一)

阿里云推出国内首个集成“快思考”与“慢思考”能力的 Qwen3-8B 大语言模型,以80亿参数和 128K 上下文支持,重塑AI应用边界并助力开发者高效部署

以微薄之力,予他人些许温暖. 2万+

数据结构—红黑树【26王道数据结构习题7.3.4第20题】

FLY_LZL的博客 114

基于截图详解:SAP GUI for Java 如何设置登录链接,以及 conn= 连接字符串该怎么写

摘要:SAP GUI for Java 的连接属性配置关键是在"高级"页签的"conn="后填写连接字符串。连接字符串格式取决于网络拓扑:直连应用服务器使用"/H/主机/S/端口";通过消息服务器负载均衡使用"/M/消息服务器/S/端口/G/组名";经SAProuter访问则需添加路由器前缀。配置时需注意专家模式会覆盖其他页签设置,且连接字符串还支持附加参数如客户端、语言和默认事务码。具体填写内容应根据Basis团队提供的系统信

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 252

数据结构与算法学习笔记7:红黑树

数据结构与算法学习笔记7:红黑树红黑树特点红黑树的创建查找(节点添加的位置)节点添加(新来节点是红色的)代码删除分析代码应用红黑树优势红黑树在Linux底层应用:Linux中的epoll模型 红黑树 AVL 绝对平衡(高度差) 红黑树 相对平衡(颜色) 红黑树是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 可以在O(log2n)O(log_2n)O(log2​n)时间内做查找,插入和删除,这里的

h0327x的博客 221

【FISCO-BCOS】 八、ubuntu配置Java环境

ubuntu配置Java环境需根据自己的java版本号进行细节修改,本文以jdk11.0.18版本为例。ps:通过此法还可以查看java安装路径,但必须是环境配置成功的情况下才能使用。二、添加Java环境路径。四、检查java环境设置。

naihe_fish的博客 4315

YOLOV8改进:添加Wiseiou,有效涨点

【代码】YOLOV8改进:添加Wiseiou,有效涨点。

AIcurator的博客 2393

计算机基础--->数据结构(7)【红黑树】

如果树是空的,创建一个新的2节点 如果树不是空的,找到应该插入的叶子节点 如果叶子节点是2节点,插入该节点,使其成为3节点。 如果叶子节点是3节点,将该节点的元素一起重新分配到新的节点中,并将中间的元素移动到父节点中。如果父节点也是3节点,就递归地进行这个过程。

weixin_56781779的博客 618

【人工智能】《Deep Dive into LLMs like ChatGPT 》—— Andrej Karpathy 第三阶段:强化学习(Reinforcement Learning, RL)

摘要:强化学习(RL)阶段通过让模型自主生成并验证答案,突破SFT阶段的模仿局限,使其在数学、代码等领域形成独特的推理路径。RL流程包括批量生成解答、自动批改筛选正确路径,并采用PPO/GRPO算法优化模型。模型会自发涌现自我检查、回滚等策略,显著提升正确率(如AIME竞赛准确率从15%升至56%)。与SFT模型相比,RL模型具备主动链式思维、验算能力,响应时间更长但效果更优,形成真正的“思考模型”。三阶段对比显示,RL通过可验证题的对错奖励机制,赋予模型超越人类范式的推理能力。

奶爸的编程之路,也就一周冷个三天 150

jdk8 HashMap红黑树学习

数据结构 jdk7内部数据结构为数组+链表,通过key的hash值计算数据所在数组下标,多个key的hash相同或hash计算的数组下标相同,但是key值不同时,往链表尾追加Entry。 transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE; static class Entry<K,V> ...

曾舜尧的专栏 2878

静态时序分析(STA)基本概念

静态时序分析中常见概念(fasle Path,multicycle path等)

m0_61544122的博客 2256

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

HashMap的底层实现原理+扩容机制+红黑树(精炼)

HashMap的底层实现原理及其扩容机制(精炼)一、HashMap的数据结构二、HashMap 键值对的存储原理和方式:三、HashMap的扩容机制Java 7 中Hashmap扩容机制Java8中HashMap扩容机制 一、HashMap的数据结构 1.它属于常用的数据结构 其中三大常用数据结构是 :数组结构、链表结构、哈希表结构。 2.优点:哈希表结构结合了 数组结构和链表结构的两大优点。添加删除快,查询也快。 二、HashMap 键值对的存储原理和方式: 1.HashMap.Node <

CKApril_java的博客 1万+

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

数据结构(7.3_5)——红黑树的插入和删除

注意:插入时候重点考察“不红红”特性(红黑树的性质) 染色:哪几个结点有变动就红的变黑,黑的变红RR、LL、LR、RL从爷结点开始算起非根结点的插入只需要判断是否违背的“不红红”特性练习:

m0_65240792的博客 347

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

数据结构(7.3_4)——红黑树的定义和性质

红黑树的二叉排序树左子树结点值<=根结点值<=右子树结点值每个结点或是红色,或是黑色的根结点是黑色的叶结点(外部结点,NULL结点失败结点)均是黑色的不存在两个相邻的红结点(即红结点的父结点和孩子结点均是黑色的)对每个结点,从该节点到任一叶结点的简单路径上,所含黑结点的数目相同口诀:左根右,根叶黑,不红红,黑路同struct RBnode {//红黑树的结点定义int key;//关键字的指针//父节点指针//左、右孩子指针int color;//结点颜色。

m0_65240792的博客 486

数据结构(7)树形结构——红黑树(概念、插入过程、删除过程)

红黑树的概念,红黑树的插入过程、红黑树的完整建树过程,红黑树的完整删除过程。

BugMan的博客 1042

【数据结构-7】二叉搜索树/AVL树/红黑树

掌握基于二叉树变化的数据结构。

lnx97111的博客 382

hashMap 红黑树

红黑树的形成(以下两个条件同时满足): 1.(容量)数组长度大于64 && 2.链表元素大于8 红黑树退化成链表:(有两种情况下会退化为链表的形式) 1.数组扩容且链表中的元素小于等于6,退化为链表 || 2.删除(remove)树节点, 当 ①root ; ②root.left; ③root.right; ④root.left.left 有一个为null时候,退化为链表 ConcurrentHashMap 1.7: 1.初始化时候开辟空间 2.Segment+数组+链表 3.

weixin_45556228的博客 1739

hashmap为什么用红黑树_要看HashMap源码,先来看看它的设计思想

HashMap 是日常开发中,用的最多的集合类之一,也是面试中经常被问到的 Java 类之一。同时,HashMap 在实现方式上面又有十分典型的范例。不管是从哪一方面来看,学习 HashMap 都可以说是有利无害的。分析 HashMap 的源码的文章在网上面已经数不胜数了,本文另辟蹊径来分析 HashMap 的设计思想。底层数据结构说到 HashMap 的数据库,我们需要从两个 JDK 版本来分析...

weixin_39587113的博客 341

HashMap底层数据结构:数组 + 链表 + 红黑树

本文将从 “数据结构演进” 的视角,深度拆解 HashMap 的底层实现 —— 从 JDK 1.7 的 “数组 + 链表”,到 JDK 1.8 的 “数组 + 链表 + 红黑树”,带你搞懂每一种结构的作用、哈希冲突的解决逻辑,以及结构演进背后的性能优化思路。

y245166349的专栏 1867

jdk7和jdk8HashMap主要的区别

1.在上篇HashMap博客中,已经提到jdk7使用的是数组+链表来实现,而jdk8使用数组+链表+红黑树实现,那为什么要引入红黑树呢,原因就是哪怕将hash的碰撞降到最低,也不能避免链表会越来越长。 [1].链表属于插入快,遍历慢的数据结构 [2].完全二叉树插入慢,遍历快 [3].红黑树插入和查询都较快 [4].jdk7中的put方法 public V put(K key, V v...

w4xj的博客 3951

《考研408数据结构》第七章(7.3 红黑树 Red-Black-Tree)复习笔记

红黑树是对平衡二叉树的优化,在保持查找效率的同时简化了插入和删除操作。红黑树通过五个核心规则(根叶黑、不红红、黑路同等)确保树结构相对平衡,其最长路径不超过最短路径的2倍。相比平衡二叉树,红黑树牺牲了严格的平衡性,换来了更高的插入和删除效率,而查找时间复杂度仍为O(log n)。红黑树的高度上限为2log₂(n+1),适用于频繁修改的场景,如Java的TreeMap实现。

m0_73991249的博客 338

HashMap的数据结构,哈希冲突,JDK1.7和JDK1.8 HashMap的区别

1.JDK1.7 HashMap是Java中大家最常用的一个map实现类,其为键值对也就是key-value的形式。他的数据结构则是采用的位桶和链表相结合的形式完成了,即拉链法。具体如下图所示: 如果两个key通过int index = hash % Entry[].length得到了相同的index(hash冲突),就会跟在之间那个entry连接在后面,也就是按照顺序存储在后面的链表中,也就是...

weixin_46106066的博客 400
上一篇: 数据结构(6)-二叉搜索树
下一篇: Java并发编程(1)-线程安全基础入门知识
BoringRong
BoringRong 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄9年 2万+粉丝 386原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BoringRong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值