二叉平衡树之红黑树

news2024/10/6 1:45:27

目录

1.概念

2.性质

3.节点的定义

4.插入

1.按照二叉搜索树规则插入结点

2.调整颜色

1.uncle存在且为红色

2.uncle不存在或者为黑    cur为

3.根节点改为黑色

5.验证

6.比较

7.应用


1.概念

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

2.性质

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

2.根节点是黑色

3.如果一个结点是红色,则他的两个孩子结点是黑色(没有两个相邻的红色结点)

4.每个结点,从该结点到其所有后代的叶子结点的路径中,含有的黑色节点个数相同

5.每个叶子结点都是黑色

为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?

最短:全黑,最长:红黑交替==>满足条件

3.节点的定义

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;
        //新增节点不能是黑色,要保证每条路上的黑色节点个数相同,会有问题:
        //1.要新增节点
        this.color=COLOR.RED;
    }

}

4.插入

1.按照二叉搜索树规则插入结点

1.创建结点

2.为空

3.遍历到当年结点为null

4.插入结点

 public boolean insert(int val){
        rbTreeNode node=new rbTreeNode(val);
        if(root==null){
            root=node;
            root.color=COLOR.BLACK;
            return true;
        }
        rbTreeNode parent=null;
        rbTreeNode cur=root;
        while(cur!=null){
            if(cur.val<val){
                parent=cur;
                cur=cur.right;
            }else if(cur.val==val){
                return false;
            }else{
                parent=cur;
                cur=cur.left;
            }
        }
        if(parent.val<val){
            parent.right=node;
           
        }else{
            parent.left=node;
        }
        node.parent=parent;
        cur=node;
    }

2.调整颜色

以parent == grand.left为例(cur,parent为红色),另一个刚好相反

g为黑色(可能为根节点,根节点为黑色),p为红色(两个连续的红色才需要调整),cur为红色(新插入的都是红色),以uncle的情况分类

1.uncle存在且为红色

p,u变为黑色 g变为红色 继续向上遍历

2.uncle不存在或者为黑    cur为

1.cur==parent.left -->右旋,p黑,g红

2.cur==parent.right -->左旋,cur和p交换,变为第一种

3.根节点改为黑色

 

情况1:cur为红,p为红,g为黑,u存在且为红

 把parent和uncle改为红色,grad改为黑色

思路: 不能有两个连着的红色结点,把p和u改为黑色,如果不把g改为红色就违背黑色节点个数相同的的性质

情况2:g为黑,u为黑,p为红,cur为红,cur为p的左孩子

情况3:g为黑,u为黑,p为红,cur为红,cur为p的右孩子

p做左单旋转,变成情况2

 public boolean insert(int val){
       RBTNode node=new RBTNode(val);
       if(root==null){
           root=node;
           root.color=COLOR.BLACK;
           return true;
       }

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


       //调整颜色
        while (parent!=null && parent.color==COLOR.RED){
            RBTNode grand=parent.parent;
            if(parent==grand.left){
                RBTNode uncle=grand.right;
                if(uncle!=null  && uncle.color==COLOR.RED){
                    //cur,p,u红,g黑
                    //方法:p.u黑,g,继续向上遍历
                    parent.color=COLOR.BLACK;
                    uncle.color=COLOR.BLACK;
                    grand.color=COLOR.RED;
                    cur=grand;
                    parent=cur.parent;
                }else{
                    if(cur==parent.right){
                        rotateLeft(parent);
                        RBTNode tmp=parent;
                        parent=cur;
                        cur=tmp;
                    }
                    rotateRight(grand);
                    parent.color=COLOR.BLACK;
                    grand.color=COLOR.RED;
                }
            }else{
                RBTNode uncle=grand.left;
                if(uncle!=null && uncle.color==COLOR.RED){
                    parent.color=COLOR.BLACK;
                    uncle.color=COLOR.BLACK;
                    grand.color=COLOR.RED;
                    cur=grand;
                    parent=cur.parent;
                }else{
                    if(cur==parent.left){
                        rotateRight(parent);
                        RBTNode tmp=parent;
                        parent=cur;
                        cur=tmp;
                    }
                    rotateLeft(grand);
                    grand.color=COLOR.RED;
                    parent.color=COLOR.BLACK;
                }
            }
        }
        root.color=COLOR.BLACK;
        return true;
    }

    private void rotateRight(RBTNode parent) {
        RBTNode subL=parent.left;
        RBTNode subLR=subL.right;
        subL.right=parent;
        parent.left=subLR;
        if(subLR!=null){
            subLR.parent=parent;
        }
        //记录原来的parent
        RBTNode pparent=parent.parent;
        parent.parent=subL;
        if(parent==root){
            root=subL;
            root.parent=null;
        }else{
            if(pparent.left==parent){
                pparent.left=subL;
            }else{
                pparent.right=subLR;
            }
            subL.parent=pparent;
        }
    }

    private void rotateLeft(RBTNode parent) {
        RBTNode subR=parent.right;
        RBTNode subRL=subR.left;
        subR.left=parent;
        parent.right=subRL;
        if(subRL!=null){
            subRL.parent=parent;
        }
        RBTNode pparent=parent.parent;
        parent.parent=subR;
        if(parent==root){
            subR=root;
            root.parent=null;
        }else{
            if(pparent.left==parent){
                pparent.left=subR;
            }else{
                pparent.right=subR;
            }
            subR.parent=pparent;
        }
    }

5.验证

红黑树的检测分为两步:

1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)

2.检测其是否满足红黑树的性质

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

2. 根节点是黑色的

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

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

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

public boolean isRBTree(){
        if(root==null){
            return true;
        }
        if(root.color!=COLOR.BLACK){
            System.out.println("违反了性质2:根节点必须是黑色的");
        }

        int blackNum=0;
        RBTNode cur=root;
        while(cur!=null){
            if(cur.color==COLOR.BLACK){
                blackNum++;
            }
            cur=cur.left;
        }
       //检查是否存在两个连续的红色节点  && 每条路径上黑色的节点的个数是一致的
        return checkRedColor(root) && checkBlackNum(root,0,blackNum);

    }

    private boolean checkBlackNum(RBTNode root, int pathBlackNum, int blackNum) {
        if(root==null) return true;
        if(root.color==COLOR.BLACK){
            pathBlackNum++;
        }
        if(root.left==null && root.right==null){
            if(pathBlackNum!=blackNum){
                System.out.println("违反了每条路径上的黑色节点的个数相同");
                return false;
            }
        }
        return checkBlackNum(root.left,pathBlackNum,blackNum)
                && checkBlackNum(root.right, pathBlackNum, blackNum);
    }

    private boolean checkRedColor(RBTNode root) {
        if(root==null) return true;
        if(root.color==COLOR.RED){
            RBTNode parent=root.parent;
            if(parent.color==COLOR.RED){
                System.out.println("违反了性质:连续出现两个红色的结点");
                return false;
            }
        }
        return checkRedColor(root.left)
                & checkRedColor(root.right);
    }
    public void inorder(RBTNode root){
        if(root==null){
            return ;
        }
        inorder(root.left);
        System.out.print(root.val+" ");
        inorder(root.right);
    }

6.比较

红黑树不追求绝对公平,只保证最长路径不超过最短路径的2倍,降低了插入和旋转的次数,在增删的结构中性能比AVL树更优

7.应用

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

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

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

相关文章

【计算机网络】计算机网络期末自测题(一)

目录 一、 填空题&#xff1a;(20 分&#xff0c;每空 1 分) 二、 选择题(20 分&#xff0c;每小题 1 分) 三、不定项选择题 (10 分&#xff0c;每小题 1 分) 四、名词解释 (15 分&#xff0c;每小题 3 分) 五、简答题 (25 分) 得分 一、 填空题&#xff1a;(20 分&#xff…

【C++】STL——string类详解

&#x1f680; 作者简介&#xff1a;一名在后端领域学习&#xff0c;并渴望能够学有所成的追梦人。 &#x1f681; 个人主页&#xff1a;不 良 &#x1f525; 系列专栏&#xff1a;&#x1f6f8;C &#x1f6f9;Linux &#x1f4d5; 学习格言&#xff1a;博观而约取&#xff0…

混合策略改进的哈里斯鹰优化算法-附代码

混合策略改进的哈里斯鹰优化算法 文章目录 混合策略改进的哈里斯鹰优化算法1.哈里斯鹰优化算法2.改进哈里斯鹰优化算法2.1 初始化种群的改进2.1.1 初始种群多样化2.1.2 初始种群精英化 2.2 逃逸能量递减机制的改进2.4 拉普拉斯交叉算子策略 3.实验结果4.参考文献5.Matlab代码6.…

6.17 、Java初级:锁

1 同步锁 1.1 前言 经过前面多线程编程的学习,我们遇到了线程安全的相关问题,比如多线程售票情景下的超卖/重卖现象. 上节笔记点这里-进程与线程笔记 我们如何判断程序有没有可能出现线程安全问题,主要有以下三个条件: 在多线程程序中 有共享数据 多条语句操作共享数据 多…

移动web-渐变

渐变 使用场景&#xff1a;使用background-image属性实现渐变背景效果 代码&#xff1a;background-image: linear-gradient(参数1,参数2,参数3...); (默认的方位从上到下) 参数1 方位名词: to right, to left 角度deg: 直接写度数 参数2 颜色1 参数3 颜色2... 注意&#xff…

看完这篇 教你玩转渗透测试靶机vulnhub—Corrosion:1

Vulnhub靶机Corrosion:1渗透测试详解 Vulnhub靶机介绍&#xff1a;Vulnhub靶机下载&#xff1a;Vulnhub靶机安装&#xff1a;Vulnhub靶机漏洞详解&#xff1a;①&#xff1a;信息收集&#xff1a; Vulnhub靶机渗透总结&#xff1a; Vulnhub靶机介绍&#xff1a; vulnhub是个提…

canvas详解01-绘制基本图形

既然我们已经设置了 canvas 环境&#xff0c;我们可以深入了解如何在 canvas 上绘制。到本文的最后&#xff0c;你将学会如何绘制矩形&#xff0c;三角形&#xff0c;直线&#xff0c;圆弧和曲线&#xff0c;变得熟悉这些基本的形状。绘制物体到 Canvas 前&#xff0c;需掌握路…

软件工程——第5章总体设计知识点整理

本专栏是博主个人笔记&#xff0c;主要目的是利用碎片化的时间来记忆软工知识点&#xff0c;特此声明&#xff01; 文章目录 1.总体设计的基本目的&#xff1f; 2.总体设计的任务&#xff1f; 3.总体设计过程由哪两个阶段组成&#xff1f; 4.总体设计的步骤&#xff1f; 5…

【Linux从入门到精通】进程地址空间(虚拟地址 vs 物理地址)

本篇文章会围绕三个问题&#xff08;什么是地址空间&#xff1f;地址空间是如何设计的&#xff1f;为什么要有地址空间&#xff1f;&#xff09;进行展开讲述。其中主要是了解虚拟地址和物理地址的区别。希望本篇文章会对你有所帮助。 文章目录 一、什么是地址空间&#xff1f;…

《机器学习公式推导与代码实现》chapter6-k近邻算法

《机器学习公式推导与代码实现》学习笔记&#xff0c;记录一下自己的学习过程&#xff0c;详细的内容请大家购买作者的书籍查阅。 k近邻算法 k近邻(k-nearest neighbor, k-NN)算法是一种经典的分类算法。k近邻算法根据新的输入实例的k个最近邻实例的类别来决定其分类。所以k近…

rust abc(1): 最小环境搭建

文章目录 1. 目的2. 命令集合3. 安装或更新 rust3.1 命令3.2 运行结果 4. 包管理工具 Cargo5. 创建 Rust 的 Hello World 程序: 单个文件6. 创建 Rust 的 Hello World 工程&#xff1a; 基于 Cargo6.1 cargo new 创建工程6.2 cargo run6.3 完整输出6.4 解释 7. IDE/编辑器8. Re…

Jetson安装Anaconda(miniforge3)

1 miniforge3 miniforge集成了Anaconda的核心工具&#xff1a;conda。conda是一个包和环境管理工具。因此&#xff0c; miniforge里面的conda和Anaconda里面的conda完全一样&#xff1b;你能用Anaconda做的安装、升级、删除包等功能&#xff0c;miniforge都能做&#xff1b;你…

angular实现自定义模块路由懒加载;配置自定义模块路由及子路由

图片中绿色表示新建的文件;黄色表示被更改的文件; 1、创建一个新的项目 ng new angularlazyload2、创建一个用户模块,并配置路由 ng g module module/user --routing如图: 3 、在module/模块下创建user组件 ng g component module/user如图: 4、实现路由懒加载 依次…

java00——类和对象

在Java中一切皆对象 什么是对象&#xff1f; 一个人、一只猫、一条狗…这些就是一个对象&#xff1b; 每个对象都有属性和行为。 什么是类&#xff1f; 类即同类别&#xff0c;例如不论男人、女人、黑人、白人…&#xff0c;都是人类&#xff0c;即同一类事务的统称。 类的…

HTB-Sandworm

HTB-Sandworm 立足altas -> silentobserversilentobserver -> 完整的atalsatlas -> rootexploit 扫描最常用的1000个端口。 80会重定向到443。 去看看443有什么吧。 目录扫描可能不会起作用。在concat上面找到了一个有趣的东西。 “如果不知道怎么PGP&#xff1…

Axure教程—折叠面板

本文介绍利用Axure中的动态面板制作折叠面板 一、效果 预览地址&#xff1a;https://3k8az1.axshare.com 二、功能 1、点击标题展开面板内容 2、点击标题折叠面板 三、制作 从默认元件库拖入一个动态面板&#xff0c;设置两个状态&#xff0c;一个状态面板标题&#xff0c;一…

【MySQL】不允许你还不了解创建计算字段

&#x1f3ac; 博客主页&#xff1a;博主链接 &#x1f3a5; 本文由 M malloc 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f384; 学习专栏推荐&#xff1a;LeetCode刷题集&#xff01; &#x1f3c5; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指…

【开源与项目实战:开源实战】82 | 开源实战三(中):剖析Google Guava中用到的几种设计模式

上一节课&#xff0c;我们通过 Google Guava 这样一个优秀的开源类库&#xff0c;讲解了如何在业务开发中&#xff0c;发现跟业务无关、可以复用的通用功能模块&#xff0c;并将它们从业务代码中抽离出来&#xff0c;设计开发成独立的类库、框架或功能组件。 今天&#xff0c;…

【后端】使用TS编写任务管理系统----Express

文章目录 常见的后端框架安装并且声明文件库项目基本配置编写任务管理后端API添加任务查看任务设置任务完成状态删除任务 总结 node -v v16.13.0https://github.com/dL-hx/server-side 常见的后端框架 expresskoa… 安装并且声明文件库 $ npm i express $ npm i types/exp…