红黑树(Java数据结构)

news2024/10/25 0:52:12

前言:

        红黑树的学习需要大家对二叉搜索树与AVL树有深刻的理解,如果话没有看过我对二叉搜索树与AVL树的讲解的铁子们可以先看看上一篇文章:二叉搜索树与AVL树(java数据结构)-CSDN博客

红黑树:

什么是红黑树?

        红黑树,是一种 二叉搜索树 ,但 在每个结点上增加一个存储位表示结点的颜色,可以是 Red Black 。 通过对 任何 一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍 ,因而是 接近 平衡 的。

 当然要满足红黑树没有想象当中的简单:

        接下来就来看看红黑树的性质:

红黑树的性质:           

1. 每个结点不是红色就是黑色
2. 根节点是黑色的
3. 如果一个节点是红色的, 则它的两个孩子结点是黑色的 【没有 2 个连续的红色节点】
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
5. 每个叶子结点都是黑色的 ( 此处的叶子结点指的是空结点 )

尤其是性质4和性质5尤为重要!!是满足一棵树是红黑树的核心!        

 红黑树结点的定义:

        

    public class RBTreeNode {
        private int val;
        private RBTreeNode left = null;
        private RBTreeNode rigth = null;
        private RBTreeNode parent;
        private COLOR color;

        public RBTreeNode(int val) {
            this.val = val;
        }
    }

红黑树的插入:

    红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

1. 按照二叉搜索的树规则插入新节点
2. 检测新节点插入后,红黑树的性质是否造到破坏
因为 新节点的默认颜色是红色 ,因此:如果 其双亲节点的颜色是黑色,没有违反红黑树任何性质 ,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点 ,此时需要对红黑树分情况来讨论:

    约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

接下来在 基础上逐步插入:

一共分为两大种情况:

1、parent = grandfather.left

2、parent = grandfather.right

每种情况中又可以分为两种情况:

1、uncle == null || uncle == black

2、uncle != null && uncle == red

接下来我以第一种parent == greandfather.left为例,剩下的一种情况与之相反!!

大家最好自己画一遍图分析得更加清楚!!

代码如下:

 public boolean insert(int val) {
        RBTreeNode node = new RBTreeNode(val);
        if (root == null) {
            root = node;
            root.color = COLOR.BLACK;
            return true;
        }
        RBTreeNode cur = root;
        RBTreeNode parent = null;
        while (cur != null) {
            if (val > cur.val) {
                parent = cur;
                cur = cur.right;
            } else if (val == cur.val) {
                return false;
            } else {
                parent = cur;
                cur = cur.left;
            }
        }
        cur = node;
        cur.parent = parent;
        if (cur.val < parent.val) {
            parent.left = cur;
        } else {
            parent.right = cur;
        }
        while(parent != null && parent.color == COLOR.RED) {
            //因为插入的节点颜色是红色,只有两个红色相邻才需要进行调整
            RBTreeNode grandfather = parent.parent;
            //需要记录g节点
            if (parent == grandfather.left) {
                RBTreeNode uncle = grandfather.right;
                //只有确定了father节点,才可以确定uncle节点
                //此时uncle节点分3种情况,
                //uncle节点存在并且为红色
                //uncle节点存在并且为黑色
                //uncle节点不存在
                if (uncle != null && uncle.color == COLOR.RED) {
                    uncle.color = COLOR.BLACK;
                    parent.color = COLOR.BLACK;
                    cur = grandfather;
                    parent = cur.parent;
                    cur.color = COLOR.RED;
                } else {
                    //uncle.color == BLACK || uncle == null
                    if (cur == parent.right) {
                        roateLeft(parent);
                        RBTreeNode tmp = parent;
                        parent = cur;
                        cur = tmp;
                    }
                    roateRight(grandfather);
                    parent.color = COLOR.BLACK;
                    grandfather.color = COLOR.RED;
                }
            } else {
                RBTreeNode uncle = grandfather.left;
                //if(parent == grandfather.right)
                //所有的结果相反
                //因为插入的节点颜色是红色,只有两个红色相邻才需要进行调整
                if (uncle != null && uncle.color == COLOR.RED) {
                    uncle.color = COLOR.BLACK;
                    parent.color = COLOR.BLACK;
                    cur = grandfather;
                    parent = cur.parent;
                    cur.color = COLOR.RED;
                } else {
                    //uncle.color == BLACK || uncle == null
                    if (cur == parent.left) {
                        roateRight(parent);
                        RBTreeNode tmp = parent;
                        parent = cur;
                        cur = tmp;
                    }
                    roateLeft(grandfather);
                    parent.color = COLOR.BLACK;
                    grandfather.color = COLOR.RED;
                }
            }
        }
        root.color = COLOR.BLACK;
        return true;
    }

红黑树的插入验证:

        当我们自己画图写好插入代码以后,接下来要做的事就是要验证我们写的代码的正确性!

当然我们应该如何验证我们的代码是正确的呢?

很简单,就从红黑树的性质开始验证,如果这棵树满足红黑树的所有的性质,呢么肯定是一棵红黑树!

性质如下:

1. 每个结点不是红色就是黑色
2. 根节点是黑色的
3. 如果一个节点是红色的, 则它的两个孩子结点是黑色的 【没有 2 个连续的红色节点】
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
5. 每个叶子结点都是黑色的 ( 此处的叶子结点指的是空结点 )

代码如下:

    public boolean isRBTree(RBTreeNode root) {
        //性质1:根节点必须是黑色
        if(root.color != COLOR.BLACK) {
            return false;
        }
        //性质3:如果一个节点是红色,那么它的两个孩子节点肯定是黑色,没有两个连续的红色节点
        //写一个方法
        if(!isRBTree1(root)) {
            return false;
        }
        //计算一条路上的黑色节点个数
        int blackSum = caluateBlackSum(root);
        //性质4:对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
        if(!isRBTree2(root,0,blackSum)) {
            return false;
        }
        return true;
    }
    public int caluateBlackSum(RBTreeNode root) {
        int num = 0;
        RBTreeNode cur = root;
        while(cur != null) {
            if(cur.color == COLOR.BLACK) {
                num++;
            }
            cur = cur.left;
        }
        return num;
    }
    public boolean isRBTree2(RBTreeNode root,int blackNum,int blackSum) {
        if(root == null) {
            return true;
        }
        if(root.color == COLOR.BLACK) {
            blackNum++;
        }
        if(root.right == null && root.left == null) {
            if(blackNum != blackSum) {
                System.out.println(root.val+"违反了性质4");
                return false;
            }
        }
        return isRBTree2(root.left,blackNum,blackSum) && isRBTree2(root.right,blackNum,blackSum);
    }
    public boolean isRBTree1(RBTreeNode root) {
        if(root == null) {
            return true;
        }
        if(root.color == COLOR.RED) {
            if(root.left != null && root.left.color == COLOR.RED) {
                System.out.println(root.val+"不满足性质3");
                return false;
            }
            if(root.right != null && root.right.color == COLOR.RED) {
                System.out.println(root.val+"不满足性质3");
                return false;
            }
        }
        return isRBTree1(root.left) && isRBTree1(root.right);
    }

当然为了保险起见,还可以写一个中序遍历验证是否满足二叉搜索树:

    public void inOrder(RBTreeNode root) {
        if(root == null) {
            return ;
        }
        inOrder(root.left);
        System.out.print(root.val+" ");
        inOrder(root.right);
    }

最后结果如下:

要经过多次尝试才可以成功啊!!!

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

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

相关文章

CenterTrack算法详解

背景&#xff1a; 早期追踪器在缺乏强的低水平线索下&#xff0c;容易失败检测后跟踪的模型依赖于检测器&#xff0c;且需要一个单独的阶段匹配关联策略的时间长 简介&#xff1a; 基于点的跟踪思想&#xff0c;通过预测目标的中心点来进行跟踪&#xff0c;同时实现检测与跟…

LLM在Reranker任务上的最佳实践?A simple experiment report(with code)

知乎&#xff1a;车中草同学(已授权)链接&#xff1a;https://zhuanlan.zhihu.com/p/987727357 引言 在BERT时代&#xff0c;对于Reranker任务&#xff0c;我们使用encoder-only的BERT为基座&#xff0c;拼接query和doc输入到BERT中去&#xff0c;在使用CLS的向量通过一个MLP&a…

身份证识别JAVA+OPENCV+OCR

一、相关的地址 https://github.com/tesseract-ocr/tessdata Releases - OpenCV opencv要装好&#xff0c;我装的是4.5.3的&#xff0c;最新版的没试过。 tessdata就下载了需要用的。好像还有best和fast的版本&#xff0c;我试了一下报错&#xff0c;不知道是不是版本不支持…

华为配置 之 远程管理配置

目录 简介&#xff1a; 知识点&#xff1a; Telnet远程管理 &#xff08;1&#xff09;配置接口IP并确保R1和R2处于同一个网段 &#xff08;2&#xff09;使用password认证模式远程登录 &#xff08;3&#xff09;使用AAA认证模式远程登录 SSH远程管理 &#xff08;1&a…

基于springboot的网上服装商城推荐系统的设计与实现

基于springboot的网上服装商城推荐系统的设计与实现 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;idea 源码获取&#xf…

【deathStarBench】2.安装k8s集群

安装docker 通过以下命令安装docker&#xff1a; sudo yum install docker-ce-26.1.4 docker-ce-cli-26.1.4 containerd.io随后通过查看docker --version&#xff0c;可以确定是否安装的版本一样 启动docker systemctl start docker && systemctl enable docker.se…

《纳瓦尔宝典:财富和幸福指南》读书随笔

最近在罗胖的得到听书中听到一本书&#xff0c;感觉很有启发&#xff0c;书的名字叫《纳瓦尔宝典》&#xff0c;从书名上看给人的感觉应该财富知识类、鸡汤爆棚哪类。纳瓦尔&#xff0c;这个名字之前确实没有听说过&#xff0c;用一句话介绍一下&#xff0c;一个印度裔的硅谷中…

【LeetCode】修炼之路-0006-Zigzag Conversion (Z 字形变换)【python】

题目 The string “PAYPALISHIRING” is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility) P A H N A P L S I I G Y I R And then read line by line: “PAHNAPLSIIGYIR” …

荣耀电脑管家-系统重装之查询设备序列号

winr输入cmd&#xff0c;再命令行中输入 wmic bios get serialnumber 如下所示

代码随想录算法训练营第六天|454四数相加II、 383赎金信、15三数之和、18四数之和

day06 1. 454四数相加II 首先定义 一个unordered_map&#xff0c;key放a和b两数之和&#xff0c;value 放a和b两数之和出现的次数。遍历大A和大B数组&#xff0c;统计两个数组元素之和&#xff0c;和出现的次数&#xff0c;放到map中。定义int变量count&#xff0c;用来统计 …

YAML格式校验API:免费工具的使用指南

YAML&#xff08;YAML Ain’t Markup Language&#xff09;是一种人类可读的数据序列化格式&#xff0c;广泛用于配置文件、数据交换等场景。由于其简洁的语法和良好的可读性&#xff0c;YAML 在开发和运维领域中越来越受到欢迎。然而&#xff0c;在使用 YAML 时&#xff0c;格…

Xcode文件默认存储位置-使用c++file保存文件默认路径以及设置为路径为当前项目路径

Xcode文件默认存储位置-使用cfile保存文件默认路径以及设置为路径为当前项目路径 1.概述 使用Xcode工具开发时候&#xff0c;遇到C调用file创建文件后&#xff0c;在当前项目中找不到文件路径。这是由于xcode会将文件保存到默认设置的路径。下面是查看文件默认存储路径和修改…

数组中的算法

目录 1.什么是数组 2.数组上的算法 2.1二分查找算法 什么是二分查找算法&#xff1f; 算法步骤 算法时间复杂度 一个问题 例题 题目分析 解题代码 2.2双指针法 什么是双指针法&#xff1f; 例题 题目分析 解题代码 1.什么是数组 数组是在一块连续的内存空间…

C++,STL 047(24.10.24)

内容 对set容器的元素进行查找与统计。 运行代码 #include <iostream> #include <set>using namespace std;void printSet(set<int> &s) {for (set<int>::iterator it s.begin(); it ! s.end(); it){cout << *it << " ";…

linux-牛刀小试

题目一&#xff1a; 1.第一问 首先创建用户tab在超级用户root的终端输入useradd tab 切换到tab用户&#xff1a; 推出重新登录到tab用户或者su – tab切换到tab用户 2.第二问 在桌面创建SHEGNCHAN目录 在SHENGCHAN文件夹下创建相应的文件&#xff1a; 3.第三问 首先&#…

哈希表【闭散列/开散列】

&#x1f33b;个人主页&#xff1a;路飞雪吖~ &#x1f320;专栏&#xff1a;C/C 目录 一、unordered系列关联式容器 &#x1f31f;unordered_map ⭐unordered_map接口说明 二、底层结构 &#x1f31f;哈希概念 &#x1f31f;哈希冲突 &#x1f31f;哈希函数 &#x1f3…

基于Leaflet和SpringBoot的全球国家综合检索WebGIS可视化

目录 前言 一、Java后台程序设计 1、业务层设计 2、控制层设计 二、WebGIS可视化实现 1、侧边栏展示 2、空间边界信息展示 三、标注成果展示 1、面积最大的国家 2、国土面积最小的国家 3、海拔最低的国家 4、最大的群岛国家 四、总结 前言 在前面的博文中&#xff…

【随手笔记】远程升级之如何平衡下载包大小与速率?

1. 远程升级基本信息 使用NB_BC26模组&#xff0c;通过AT指令使用TCP的协议与公司后台交互升级的固件为BIN文件&#xff0c;使用原始固件包升级&#xff0c;未使用差分方式原始固件包有110K,大小左右&#xff0c;固件的存储为外置的FLASH W25Q16,w25q16最小存储单位为页&#…

AListFlutter(手机alist)——一键安装,可在手机/电视上运行并挂载各个网盘

前面提到软路由系统OpenWRT的时候&#xff0c;当时说过可以在OpenWRT里安装alist&#xff0c;然后挂载网盘&#xff0c;这样就可以通过webdav的方式在家庭局域网下的任何设备都可以访问操作这些网盘&#xff0c;摆脱硬盘空间不够的问题。 但alist的官方版本是没有手机版本的&a…

【Java】探秘正则表达式:深度解析与精妙运用

目录 引言 一、基本概念 1.1 元字符 1.2 预定义字符类 1.3 边界匹配符 1.4 数量标识符 1.5 捕获与非捕获分组 二、Java中的正则表达式支持 三、正则表达式的使用示例 3.1 匹配字符串 3.2 替换字符串 3.3 分割字符串 3.4 使用Pattern和Matcher 3.5 捕获组和后向…