数据结构之红黑树的 “奥秘“

news2024/12/22 13:59:13

目录:

一.红黑树概念

二. 红黑树的性质

.红黑树的实现

四.红黑树验证 

五.AVL树和红黑树的比较

一.红黑树概念

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



二. 红黑树的性质:

1. 每个结点不是红色就是黑色

2. 根节点是黑色的

3. 如果一个节点是红色的,则它的两个孩子结点是黑色的【没有2个连续的红色节点】

4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点也就是(每条路径的黑色节点数相等)

5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

总结性质:最长路径最多是最短路径的2倍.

总结性质推导:



.红黑树的实现:

1.红黑树节点的定义 :

这里注意我们定义一个枚举来储存红黑树节点的颜色

public class RBTree {
    static class RBTreeNode {
        public RBTreeNode left;
        public RBTreeNode right;
        public RBTreeNode parent;
        public int val;
        public COLOR color;//枚举

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

            //新创建的节点默认是红色
            this.color = COLOR.RED;
        }
    }
    public RBTreeNode root;
}

 

2.红黑树的插入:

这里我们要围绕红黑树上面的几条性质构建红黑树;但是红黑树是在二叉搜索树的基础上加上其平衡限制条件,所有我们构建时可以借鉴二叉搜索树方式。

步骤一:和二叉二叉搜索树一样找到要插入的节点;

步骤二:调整插入的节点让其满足红黑树的性质;

所有我们构建红黑树总共有三种情况

这里注意:插入节点默认为红色节点,推导如下:

3.构建红黑树的有三种情况:

3.1.情况一: cur为红,p为红,g为黑,u存在且为红:

图解:

代码:

 //开始调整颜色
        while (parent != null && parent.color == COLOR.RED) {
            RBTreeNode grandParent = parent.parent;

            /**情况一:
             *
             * cur为红,p为红,g为黑,uncle存在且为红
             *
             *  parent在grandParent左边,uncle在grandParent右边
             */
            if (parent == grandParent.left) {
                RBTreeNode uncle = grandParent.right;
                if (uncle != null && uncle.color == COLOR.RED) {
                    parent.color = COLOR.BLACK;
                    uncle.color = COLOR.BLACK;
                    grandParent.color = COLOR.RED;

                    //预防grandParent的父亲为红色,就还有子树,继续向上修改
                    cur = grandParent;
                    parent = cur.parent;
                }

3.2.情况二: cur为红,p为红,g为黑,u不存在或者u为黑:

这里注意要先grandParent右旋,然后再调整颜色,parent改为 黑色,grandParent改为红色

 图解:

代码:

/** 情况二:
* cur为红,p为红,g为黑,uncle为黑色,或者uncle不存在
*
*  方法:
*  1.先右单旋
*  2.再改颜色
 */
rotateRight(grandParent);
parent.color = COLOR.BLACK;
grandParent.color = COLOR.RED;

3.3.情况三: 调整过程中,cur为红,p为红,g为黑,u不存在/u为黑:

这里先左旋parent,再把parent 和 cur 的引用交换变为和情况二类似,再当作情况二处理(右旋改颜色,图片上笔误是右旋

代码:

/**
 * 情况三:
* 先左单旋parent
* 再交换parent和cur的引用,变成情况二处理
*/
if (parent.right == cur) {
rotateLeft(parent);
RBTreeNode tmp = parent;
parent = cur;
cur = tmp;

}//变成情况二

 

当parent == grandParent.right,和上面三种情况完全相反,为镜相关系。

插入全部代码如下:

public class RBTree {
    static class RBTreeNode {
        public RBTreeNode left;
        public RBTreeNode right;
        public RBTreeNode parent;
        public int val;
        public COLOR color;//枚举

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

            //新创建的节点默认是红色
            this.color = COLOR.RED;
        }
    }
    public RBTreeNode root;

    //插入:
    public boolean insert(int val) {
        RBTreeNode node = new RBTreeNode(val);

        if (root == null) {
            root = node;
            //插入节点默认为红色所有,当root为空时,要把插入的节点变为黑色
            root.color = COLOR.BLACK;
            return true;
        }

        RBTreeNode cur = root;
        RBTreeNode parent = null;
        while (cur != null) {
            if (cur.val < val) {
                parent = cur;
                cur = cur.right;
            } else if (cur.val > val) {
                parent = cur;
                cur = cur.left;
            } else {
                return false;
            }
        }

        if (parent.val < val) {
            parent.right = node;
        } else {
            parent.left = node;
        }

        node.parent = parent;
        cur = node;//指向新插入的节点

        //开始调整颜色
        while (parent != null && parent.color == COLOR.RED) {
            RBTreeNode grandParent = parent.parent;

            /**情况一:
             *
             * cur为红,p为红,g为黑,uncle存在且为红
             *
             *  parent在grandParent左边,uncle在grandParent右边
             */
            if (parent == grandParent.left) {
                RBTreeNode uncle = grandParent.right;
                if (uncle != null && uncle.color == COLOR.RED) {
                    parent.color = COLOR.BLACK;
                    uncle.color = COLOR.BLACK;
                    grandParent.color = COLOR.RED;

                    //预防grandParent的父亲为红色,就还有子树,继续向上修改
                    cur = grandParent;
                    parent = cur.parent;
                } else {

                    /**
                     * 情况三:
                     * 先左单旋parent
                     * 再交换parent和cur的引用,变成情况二处理
                     */
                    if (parent.right == cur) {
                        rotateLeft(parent);
                        RBTreeNode tmp = parent;
                        parent = cur;
                        cur = tmp;

                    }//变成情况二


                    /** 情况二:
                     * cur为红,p为红,g为黑,uncle为黑色,或者uncle不存在
                     *
                     *  方法:
                     *  1.先右单旋
                     *  2.再改颜色
                     */

                    rotateRight(grandParent);

                    parent.color = COLOR.BLACK;
                    grandParent.color = COLOR.RED;
                }
            } else {


                //下面情况和上面情况完全相反


                //parent == grandParent.right
                RBTreeNode uncle = grandParent.left;
                if (uncle != null && uncle.color == COLOR.RED) {
                    parent.color = COLOR.BLACK;
                    uncle.color = COLOR.BLACK;
                    grandParent.color = COLOR.RED;

                    //预防grandParent的父亲为红色,就还有子树,继续向上修改
                    cur = grandParent;
                    parent = cur.parent;
                } else {

                    if (parent.left == cur) {
                        rotateRight(parent);
                        RBTreeNode tmp = parent;
                        parent = cur;
                        cur = tmp;

                    }
                    //变成情况二

                    rotateLeft(grandParent);
                    parent.color = COLOR.BLACK;
                    grandParent.color = COLOR.RED;
                }
            }
        }

        //当parent为空时,要把根节点变为黑色
        root.color = COLOR.BLACK;
        return true;
    }


        /**
         * 右单旋
         * @param parent
         */
        private void rotateRight (RBTreeNode parent){
            RBTreeNode subL = parent.left;
            RBTreeNode subRL = subL.right;

            parent.left = subRL;
            subL.right = parent;
            //如果旋转的整棵树也是一个子树,记录下原来该树的父亲,后续修改
            RBTreeNode pParent = parent.parent;

            if (subRL != null) {
                subRL.parent = parent;
            }
            parent.parent = subL;

            //看看整棵树是否也是一个子树
            if (parent == root) {
                root = subL;
                root.parent = null;
            } else {
                //是子树就确定这棵树是左子树还是右子树
                if (pParent.left == parent) {
                    pParent.left = subL;
                } else {
                    pParent.right = subL;
                }
            }
            subL.parent = pParent;

        }

        /**
         * 左单旋
         * @param parent
         */
        private void rotateLeft (RBTreeNode parent){
            RBTreeNode subR = parent.right;
            RBTreeNode subRL = subR.left;

            parent.right = subRL;
            subR.left = parent;
            RBTreeNode pParent = parent.parent;

            if (subRL != null) {
                subRL.parent = parent;
            }
            parent.parent = subR;

            //看看整棵树是否也是一个子树
            if (parent == root) {
                 root = subR;
                root.parent = null;
            } else {
                //是子树就确定这棵树是左子树还是右子树
                if (pParent.left == parent) {
                    pParent.left = subR;
                } else {
                    pParent.right = subR;
                }
            }
            subR.parent = pParent;
        }
}


四.红黑树验证:

1.红黑树的检测分为两步:

步骤一: 检测其是否满足二叉搜索树(中序遍历是否为有序序列)

步骤二:检测其是否满足红黑树的性质 

 

步骤一: 检测其是否满足二叉搜索树(中序遍历是否为有序序列):

代码:

 /**1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
     * 中序遍历:
     * @param root
     */
    public void inorder(RBTreeNode root){
        if(root == null){
            return;
        }

        inorder(root.left);
        System.out.print(root.val+ " ");
        inorder(root.right);
    }

步骤二:检测其是否满足红黑树的性质 :

//2.检测其是否满足红黑树的性质:
    public boolean isRBTree(){
        if(root == null){
            //空树也是红黑树
            return true;
        }

        if(root.color != COLOR.BLACK){
            System.out.println("违反了性质2:根节点不是黑色");
            return false;
        }


        RBTreeNode cur = root;
        //blackNum是事先计算好一边黑色节点的个数
        int blackNum = 0;
        while (cur != null){
            if (cur.color == COLOR.BLACK){
                blackNum++;
            }
            cur = cur.left;

        }
        //判断性质三有没有两个红色的节点 && 判断性质四:每条路径的黑色节点个数是否相等
        return checkRedColor(root) && checkBlackNum(root,blackNum,0);
    }

    /**
     * 判断性质三有没有两个红色的节点:
     * 思路:遍历当前二叉树节点如果是红色,则判断他的父亲节点是不是红色
     * @param root
     * @return
     */
    private boolean checkRedColor(RBTreeNode root){
        if(root == null){
            return true;
        }

        if (root.color == COLOR.RED){
            RBTreeNode parent = root.parent;
            if (parent != null && parent.color == COLOR.RED){
                System.out.println("违反了性质三: 连续出现两个红色的节点");
                return false;
            }
        }
       return checkRedColor(root.left) && checkRedColor(root.right);
    }


    /**
     *判断性质四:每条路径的黑色节点个数是否相等
     * @param root
     * @param blackNum:事先计算好黑色节点的个数
     * @param pathBlackNum:每次递归计算的黑色节点的个数
     * 思路:看 blackNum 和 pathBlackNum 的数量是否相等
     * @return
     */
    private boolean checkBlackNum(RBTreeNode root,int blackNum, int pathBlackNum){
        if(root == null){
            return true;
        }

        if (root.color == COLOR.BLACK){
            pathBlackNum++;
        }

        //blackNum 和 pathBlackNum 的数量是否相等就不满足性质
        if (root.left == null && root.right == null){
            if(pathBlackNum != blackNum){
                System.out.println("违反了性质四:每条路径的黑色节点个数不相等了!");
                return false;
            }
        }
        return checkBlackNum(root.left,blackNum,pathBlackNum)
                && checkBlackNum(root.right,blackNum,pathBlackNum);
    }

  



五.AVL树和红黑树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log2^n),红黑树不追求绝对平衡,其只需保 证最长路径不超过最短路径的2倍(相对平衡),相对而言,降低了插入和旋转的次数,所以红黑树在经常进行增删的结构中性能比 AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。 

 

补充:java集合框架中的:TreeMap、TreeSet底层使用的就是红黑树

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

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

相关文章

03 Flask-添加配置信息

回顾之前学习的内容 02 Flask-快速上手 Flask 中最简单的web应用组成 1. 导入核心库 Flask from flask import Flask2. 实例化 web应用 注意&#xff1a;不要漏了 app Flask(__name__) 中的 __name__ 表示&#xff1a;是从当前的py文件实例化 app Flask(__name__)3. 创…

内网隧道:端口转发

目录 LCX端口转发 场景一 场景二 SSH的端口转发 一、本地转发&#xff08;正向访问A&#xff09;&#xff1a; 二、远程转发&#xff08;反向访问A&#xff09; 三.NETSH端口转发 端口转发和端口映射 端口转发,有时被称为做隧道,是安全壳( SSH)为网络安全通信使用的一种方…

视频监控接入平台web客户端有时无法登录,有时打开实时视频出现黑屏的问题解决

目录 一、背景说明 二、解决过程 1、问题产生 2、命令介绍 ①基本用法 ②常用选项 ③示例 3、问题解决 三、最终结果 一、背景说明 在本地登录视频监控平台的服务器进行测试时&#xff0c;发现客户端登录不上。 检查服务器的服务和数据库&#xff0c;运行状况正常&#xff0c…

45个图源二维码分享及使用方法

我们曾在《40个图源二维码分享及使用方法》一文中&#xff0c;为你分享了40个图源二维码。 现在在此基础之上新增5个图源二维码&#xff0c;共分享45个。 如果你需要这些图源&#xff0c;请在文末查看领取方式。 45个图源 打开下面的网址进入水经微图&#xff08;简称“微图…

Swift 创建扩展(Extension)

类别(Category) 和 扩展(Extension) 的 用法很多. 常用的 扩展(Extension) 有分离代码和封装模块的功能,例如登陆页面有注册功能,有登陆功能,有找回密码功能,都写在一个页面就太冗余了,可以考虑使用 扩展(Extension) 登陆页面的方法来分离代码 本文介绍Swift 如何创建扩展(Ex…

maven项目下使用Jacoco测试覆盖率

【本文前提是了解maven项目及其Pom.xml机制&#xff0c;不熟悉可以看该博客的0.Pre部分Auto-Unit-Test-Case-Generator -- java项目自动测试生成-CSDN博客】 JaCoCo&#xff08;Java Code Coverage&#xff09;是一个开源的代码覆盖率工具&#xff0c;专门用于测量 Java 应用程…

Web测试中如何简单定位Bug

定位bug之前要确定自己对用例的理解是否有问题。&#xff08;在工作中,很多测试结果错误都是因为自己对用例的理解没有到位&#xff0c;以致于操作错误导致结果不符合预期&#xff09; 一般来说bug分为前端bug和后端bug&#xff0c;前端bug为请求数据错误&#xff0c;后端bug为…

web基础之信息泄露

1、目录遍历漏洞 &#xff08;1&#xff09;原理&#xff1a;本质是没有过滤用户输入的 ../ 相关的目录跳转符&#xff0c;使得攻击者通过目录跳转符来遍历服务器中的任意文件。 &#xff08;2&#xff09;题解&#xff1a; eg:根据提示遍历网页目录信息&#xff0c;会在某一个…

vscode---snippets配置全局代码片段,快捷开发!

代码片段的作用&#xff1a;在开发一个项目时&#xff0c;经常会遇到好多同一个代码逻辑&#xff0c;可配置固顶逻辑的代码块&#xff0c;避免重复敲同一代码&#xff1b; 举例&#xff1a;比如跳转登录&#xff0c;需要调用app的客户端方法&#xff0c;api调用跳转&#xff1…

Web 原生组件化方案:Web Components

你好&#xff0c;我是沐爸&#xff0c;欢迎点赞、收藏、评论和关注。 Web 组件化是一种将Web应用的UI部分拆分成可复用的独立组件的架构方法。这种方法有助于提高代码的可维护性、可重用性和可测试性。 而Web Components 标准则提供了一套原生的API&#xff0c;允许开发者创建…

TestCraft - GPT支持的测试想法生成器和自动化测试生成器

在当今快速变化的软件开发世界中&#xff0c;自动化测试已成为确保软件质量的关键环节。而随着AI技术的进步&#xff0c;越来越多的工具开始引入人工智能&#xff0c;来辅助生成测试用例和自动化测试脚本。其中&#xff0c;TestCraft&#xff0c;作为一款GPT支持的测试想法生成…

天命所归,SyntaxFlow助大圣取得真经

之前预告许久的SyntaxFlow功能已经登陆Yakit&#xff01; SyntaxFlow代码查询需要先进行项目编译。 手动编译 在前端的YakRunner界面&#xff0c;主界面或选项栏可以直接点击“编译项目”功能。 可见图中红色方框圈起的选项 编译项目的选项如下&#xff1a;必选项为项目名、…

工控机防病毒/防勒索病毒如何一步搞定?

随着勒索病毒的肆虐和内部运营泄密事件的频发&#xff0c;企业数据安全正面临着前所未有的挑战。苏州深信达网络科技有限公司&#xff0c;作为数据安全解决方案的先驱&#xff0c;推出了MCK主机加固解决方案&#xff0c;为企业数据安全提供了一道坚不可摧的防线。 MCK主机加固…

Linux:多路转接 select、poll、epoll

目录 1&#xff1a;select 1. 参数解释 2. 函数返回值 3. fd_set 4. fd_set 相关接口 5. timeval 5. 常见使用 6. 理解 select 执行过程 7. select 的特点 8. select 缺点 9. select 应用 2&#xff1a;socket 就绪条件 1. 读事件就绪&#xff08;Readable&#x…

智能优化算法-海马优化算法(SHO)(附源码)

目录 1.内容介绍 2.部分代码 3.实验结果 4.内容获取 1.内容介绍 海马优化算法 (Seahorse Optimization Algorithm, SHO) 是一种基于群体智能的元启发式优化算法&#xff0c;它模拟了海马的觅食行为、繁殖行为以及社会互动&#xff0c;用于解决复杂的优化问题。 SHO的工作机制…

精选干货!分享5款ai智能写论文软件

在当今信息爆炸的时代&#xff0c;AI智能写作工具已经成为我们写作过程中的得力助手。特别是对于学术论文的撰写&#xff0c;这些工具不仅能够提高写作效率&#xff0c;还能帮助用户生成高质量的文稿。以下是五款值得推荐的AI智能写论文软件&#xff0c;其中特别推荐千笔-AIPas…

Path系统环境变量和CLASSPATH环境变量

Path系统环境变量 概述&#xff1a;Path环境变量不是java的&#xff0c;它隶属于windows操作系统 作用&#xff1a; PATH环境变量实际上就是给windows操作系统指路的。 在Path环境变量中有很多路径&#xff0c;路径和路径之间采用 分号(;) 隔开在DOS命令窗口中输入一条DOS命…

Vscode中搭建ABAP开发环境

文章目录 前提&#xff08;在SAP系统中测试&#xff09;1.1 登录sap 系统1.2激活测服务测试1.3 添加服务 下载Vscode2.1 安装ABAP Remote filesystem 打开ABAP System3.1 按照CtrlshiftP 找到AbapFs Connect to an ABAP system 前提&#xff08;在SAP系统中测试&#xff09; 1…

2-89 基于matlab的图像去噪方法

基于matlab的图像去噪方法&#xff0c;对比了常见的几种去噪方法&#xff0c;含中值滤波&#xff0c;均值滤波&#xff0c;维纳滤波&#xff0c;高斯滤波&#xff0c;以及三种形态学滤波&#xff08;一般的&#xff0c;改进的&#xff0c;多结构元素形态学滤波&#xff09;&…

HarmonyOS开发之Tab样式(背景高亮样式)

一&#xff1a;开发环境 二&#xff1a;效果图 三&#xff1a;实现步骤 Entry Component struct TabsPage {State tabArray:string[] ["首页","分类","应用","热点","我的"]State focusIndex: number 0;State index: num…