当前位置: 首页 > news >正文

【数据结构与算法】第六篇:红黑树

🥳知识导航

  • 一.维护红黑树的五条性质
  • 二、红黑树与四阶b树的等价变换
  • 三.简单的继承关系
  • 四.重新定义红黑树节点
  • 四.红黑树添加后的调整
    • 1. 不会发生上溢 情况
      • (1)LL与RR情况
      • (2)LR与RL情况
    • 2. 会发生上溢情况
  • 五.四.红黑树删除后的调整


一.维护红黑树的五条性质

在这里插入图片描述

红黑树与AVL树一样都是一颗自平衡二叉树,都能自主达到平衡。AVL树的平衡是判断左右子树的高度差的绝对值是否小于1.而红黑树的平衡只需保证它的以下几个性质达到满足即可:
1.节点是RED或者BLACK
2.根节点是BLACK节点
3.不能出现连续两个红色节点(可以出现连续两个黑色节点)
4.RED节点的子节点和父节点都是BLACK
5. 从任一节点到叶子节点的所有路径都包含相同数目的 BLACK 节点
6.叶子节点都是BLACK,(问上图叶子节点看着不全是黑色?答:这里所说的黑色节点是假想的黑色节点)

在这里插入图片描述

上面这几个性质不难理解,那么有人问了,为什么满足这几个性质就一定是平衡的?

从b树角度便可以很容易理解这个问题,因为红黑树等价于四阶b树,满足那几个性质的树就是一个红黑树,等价于四阶b树,b树一定是平衡的。

二、红黑树与四阶b树的等价变换

在这里插入图片描述
1.黑色节点与它的红色子节点合并看成一个b树节点,红黑树就类比转化成为了一颗b树。
2.红黑树的 BLACK 节点个数 与 4阶B树的节点总个数 相等

三.简单的继承关系

在这里插入图片描述
具体代码细节可移步代码仓库—>代码仓库

四.重新定义红黑树节点

理清亲子关系

//自定义内部类-->红黑树类型
    public static class RBNode<E>extends Node<E>{
        boolean color=RED;
        //为什么默认为红色:因为默认为红色的情况下对于红黑树的几个条件除了
        //出现连续红色节点可能不满足,其余条件都满足,所以更加的适合将红色设置为默认颜色
        //更容易使红黑树的性质得到满足
        //4不满足的可能情况使出现两个连续的红色子节点
        public RBNode(E element, Node<E> parent) {
            super(element, parent);
        }

        //内部类重新定义一个方法--->返回兄弟节点
        //不用定义返回叔父节点-->因为node.parent.sibling()-->就是叔父节点
        public Node<E> sibling()
        {
            if(isLeftChild())
            {
                return parent.right;
            }
            if(isRightChild()){
                return parent.left;
            }
            return null;
        }

        @Override
        public String toString() {
           String str="";
           if(color==RED){
               str="R_";
           }
          return str+element.toString();

        }
    }

为什么默认为红色:因为默认为红色的情况下对于红黑树的几个条件除了出现连续红色节点可能不满足,其余条件都满足,所以更加的适合将红色设置为默认颜色.

因为默认为红色节点,并且要规避连续两个红色的情况.所以添加的时候需要对所有可能添加的位置进行分类,添加节点的父节点为黑色时无需处理直接添加即可,如果添加节点的父节点是红色,则应该按情况处理双红的局面

四.红黑树添加后的调整

有 4 种情况满足红黑树的性质 4 :parent 为 BLACK
同样也满足 4阶B树 的性质
因此不用做任何额外处理
在这里插入图片描述剩余8个可以添加的位置,是不符合红黑树性质的—>会出现连续红色的现象.需要对其进行修复。这八种又分为两种,有四种会发生B树节点上溢的情况。
咱们先看不需要解决上溢的四种情况:🤩

1. 不会发生上溢 情况

在这里插入图片描述

(1)LL与RR情况

解救方案:将parent染成黑色,grand染成红色,然后进行旋转(LL进行右旋转,RR进行左旋转)
在这里插入图片描述

(2)LR与RL情况

  1. 自己染成 BLACK ,grand 染成 RED
  2. 进行双旋操作
    3.LR:parent 左旋转, grand 右旋转
    4.RL:parent 右旋转, grand 左旋转

在这里插入图片描述

2. 会发生上溢情况

10为红色节点,肯定不会单独成为一颗子树,一定会向上合并,一旦合并,对于四阶B树节点,达到四个元素就会发生上溢,其余三种情况也是如此
在这里插入图片描述

LL情况
类比上一节的B树,当B树节点发生上溢后的解决办法为
(找出节点的中间元素向上合并,然后中间元素两边元素分裂为两个子树。)

所以解决办法为,将parent,uncle节点染成黑色,grand节点染成红色,grand节点向上合并(当作新添加的节点,重复利用代码)。
在这里插入图片描述◼ grand 向上合并时,可能继续发生上溢
◼ 若上溢持续到根节点,只需将根节点染成 BLACK

RR情况
在这里插入图片描述

  1. parent、uncle 染成 BLACK
  2. grand 向上合并
  3. 染成 RED ,当做是新添加的节点进行处理

LR,RL情况
解决方法同上面一样
在这里插入图片描述添加调整代码

 /**
     * 添加:如果第一次添加的是根节点,但是默认颜色为红色,所以需要将其染黑
     *
     *                          |-----红---黑---红
     *       |                   |                |                  |
     *  红---黑---红            黑--红           红--黑               黑
     *        4                  3               3                  2
     * 1.有四种情况直接满足红黑树的性质,父节点parent为黑节点--->不可能造成两个红节点连续的情况
     * 2.其余八种父节点(parent)需要特殊处理---又按照叔父节点是红色和叔父节点是黑色划分,叔父节点是红色的会发生上溢
     * 。但是叔父节点为黑色另有不同--->LL RR LR RL (grand进行单旋)
     * @param node 新添加的节点->太秒了
     */
    @Override
    protected void afterAdd(Node<E> node) {
        Node<E> parent=node.parent;
        //这个代码块包括两种情况--->(1)第一次添加节点(2)叔父节点是红色的情况下,上溢到了根节点
        if(parent==null){
            black(node);
            root=node;
            return;
        }
        if(isBlack(parent))
        {
            //排除本身就满足条件的哪四种情况,不需要额外处理
            return;
        }
        Node<E> uncle=parent.sibling();
        Node<E> grand=parent.parent;
        //叔父节点是红色-->发生上溢
        if(isRed(uncle)){
            //上溢分裂的情况
            black(parent);
            black(uncle);
            //把祖父节点当成一个新添加的节点加到上面-->解决上溢
            afterAdd(red(grand));
            return;
        }
        //叔父节点不是红节点
        if(parent.isLeftChild())
        {
            //由于染色顺序并不影响所以可以把统一的染色顺序放在前面red(grand)
            //LL RR 单旋
            if(node.isLeftChild()){//LL
                black(parent);
                red(grand);
            }else {//LR
                black(node);
                red(grand);
                rotateLeft(parent);
            }
            rotateRight(grand);
        }else{
            if(node.isRightChild())//RR
            {
                black(parent);
                red(grand);
            }else{//RL
                black(node);
                red(grand);
                rotateRight(parent);
            }
            rotateLeft(grand);
        }
    }

五.四.红黑树删除后的调整

删除代码过繁杂,可以移步代码仓库的删除代码逻辑梳理图解–>gitee代码仓库

删除后调整代码

 //删除黑色节点要传入黑色节点的替代节点
    @Override
    protected void afterRemove(Node<E> node, Node<E> replacement) {
        //b树类比红黑树一定是删除的最后一层。如果要删除的是红色,由于黑色是主体,
        // 所以要删除的节点一定度为0的红色叶子节点,不会影响原本红黑树的规则,所以不用调整,直接结束函数
        if(isRed(node)){
            return;
        }
        /**
         * 一定要清楚afterRemove的作用是删除之后的调整,不是主体删除方法。
         * 作用:对于不符合红黑树性质的操作及时进行调整
         *
         * 问:对于删除度为2的黑色节点,是不是可是囊括
         * 答:可以:因为删除度为2的黑色节点,找到前驱后,还是删除红色节点不用处理,染黑替代节点并不影响,因为已经删除
         *
         * 为什么黑色节点的替代不能为黑色节点
         * 因为b树的删除都是在最后一层进行操作,如果黑色节点子节点依然是一个黑色节点,由于黑色才是主体
         * 不会融入到一个节点中,所以只会向下增高一层,不符合删除在最后一层操作。,
         */
        if(isRed(replacement)){
            //染黑:避免出现连续两个红色节点
            black(replacement);
            return;
        }
        /**
         * 处理最后一种情况-->删除度为0的黑色叶子节点的调整方法
         *
         * 删除度为0的黑色叶子节点的调整方法肯定会造成下溢
         * 补救:(1)看看兄弟节点能不能借
         * a:兄弟节点能借的条件:1.兄弟节点是黑色 2.兄弟节点至少拥有一个红色子节点
         * ----------------------------------------
         * 能借后做出的操作:
         * 进行旋转操作
         * 旋转之后的中心节点继承 parent 的颜色
         * 旋转之后的左右节点染为 BLACK(因为此时红色子节点已经旋上去了)
         *
         * 旋转有三种情况:LL LR LL
         *
         * 🤩🤩特殊情况1:
         * 如果被删除的是黑色,它的父节点也是黑色,删除后黑色父节点也会向下合并,造成父节点继续下溢
         *只需将这个父节点当成一个新的被删除的黑色节点递归调用afterRemove方法,重复利用代码
         *
         * 🤩🤩特殊情况2:
         * 🚩🚩🚩如果兄弟节点是红色,对于b树的性质,【黑色节点与其红色节点组成一个b树节点】
         * 可知:红色节点一定在父节点中-->红色的节点的子节点一定是黑的,可以借侄子的【想办法将侄子变成兄弟】
         * 强制将侄子变成兄弟再次套用兄弟节点是黑色是的代码
         *-----------------------------------------------
         */

        Node<E> parent=node.parent;
        if(parent==null)
        {
            return;
        }
        //这么写有问题:node已经被删除--->指向node的线已经断了
        //Node<E> sibling =node.sibling();
        /**
         *  需要间接求出兄弟节点
         *  只需知道被删除的节点先前是在parent的左边还是右边,进而知道sibling是parent.left还是parent.right
         */

        /**
         *    为什么以此作为判定标准:
         *         因为在二叉搜索树的删除中,删除黑色叶子节点,已经将一边置为null
         *         if (node == node.parent.left) {
         *             node.parent.left=null;
         *         } else { // node == node.parent.right
         *             node.parent.right=null;
         *         }
         */
        //有特殊情况
        boolean left=parent.left==null||node.isLeftChild();
        Node<E> sibling=left?parent.right:parent.left;
        if(left){//node是在左子树,兄弟节点在右边

            if(isRed(sibling)){
                black(parent);
                red(sibling);
                rotateLeft(parent);
                //更新兄弟
                sibling=parent.right;
            }

            //兄弟节点是黑色-->判断是否有红色子节点

            //没有一个红色子节点--->父节点向下合并

            /**
             * 为什么以父节点是不是黑的为判定条件
             * 因为如果父节点是红色,那它肯定不是主体,向下合并后,parent本身并没有下溢
             * 如果父节点是黑色,那他向下合并后,自身也会下溢
             *
             */
            if(isBlack(sibling.left)&&isBlack(sibling.right)){
                boolean parentBlack=isBlack(parent);
                black(parent);
                red(sibling);
                //这种情况父节点向下合并,父节点的位置也会下溢
                if(parentBlack){
                    //三黑局面没有替代节点
                    afterRemove(parent,null);
                }

            }else {//至少有一个红色子节点
                //判断是LL LR.....
                //三种情况:LL LR (LL或LR)
                //可以先统一进行左旋转然后在统一进行右旋转
                //如何区分LR左子节点是黑的
                if(isBlack(sibling.left)) {//LR情况

                    rotateLeft(sibling);
                    //左旋转后兄弟节点已经发生了变化
                    sibling = parent.left;
                }
                //LL 情况
                //现在的sibling已经更新过是图中的78
                color(sibling,iscolor(parent));
                black(parent);
                black(sibling.left);
                rotateRight(parent);
            }

        }else {//node是在右子树,兄弟节点在左边
            //🤩🤩特殊情况2:
            if(isRed(sibling)){
                black(parent);
                red(sibling);
                rotateRight(parent);
                //更新兄弟
                sibling=parent.left;
            }

            //兄弟节点是黑色-->判断是否有红色子节点

            //没有一个红色子节点--->父节点向下合并

            /**
             * 为什么以父节点是不是黑的为判定条件
             * 因为如果父节点是红色,那它肯定不是主体,向下合并后,parent本身并没有下溢
             * 如果父节点是黑色,那他向下合并后,自身也会下溢
             *
             */
            if(isBlack(sibling.left)&&isBlack(sibling.right)){
                boolean parentBlack=isBlack(parent);
                black(parent);
                red(sibling);
                //这种情况父节点向下合并,父节点的位置也会下溢
                if(parentBlack){
                    //三黑局面没有替代节点
                    afterRemove(parent,null);
                }

            }else {//至少有一个红色子节点
                //判断是LL LR.....
                //三种情况:LL LR (LL或LR)
                //可以先统一进行左旋转然后在统一进行右旋转
                //如何区分LR左子节点是黑的
                if(isBlack(sibling.left)) {//LR情况

                    rotateLeft(sibling);
                    //左旋转后兄弟节点已经发生了变化
                    sibling = parent.left;
                }
                //LL 情况
                //现在的sibling已经更新过是图中的78
                color(sibling,iscolor(parent));
                black(parent);
                black(sibling.left);
                rotateRight(parent);
            }

        }

红黑树整体代码

package Tree;


import java.util.Comparator;

/**
 * 回忆红黑树应该有哪些操作
 * 1.染色(公众接口)(1)染红(2)染黑
 * 2.旋转:在不是红黑树的时候调整
 * 3.RBNode类。增加颜色属性-->不需要高度,因为红黑树和AVL树是两回事,没有平衡因子的概念
 * 4.重写afterAdd()方法,afterRemove()方法
 * ...............
 * 注意:RB树相当于四阶b树,所以节点元素个数可以为[1,3],b树添加第一个元素后,下一个元素仍旧是添加到这个节点。
 * b树的元素添加必须是叶子节点,第一次添加叶子节点就是根节点
 *--------------------------------------------------------------------------------------
 * 红黑树由五条性质定义
 * 1.节点只有RED和BLACK两种
 * 2.根节点必须是BLACK
 * 3.叶子节点都是BLACK--->注意这里说的叶子节点说的是虚拟假想的空节点
 * 4.RED子父节点都是BLACK节点。-->注意:RED树不可能出现两个红色节点连续的情况,但是会出现两个黑节点的情况
 * 5.从任意节点到叶子节点都有相同数目的黑节点(BLACK)
 * 注意:在判定是否是红黑树的时候一定要注意不要忘了虚拟的黑节点。
 * ----------------------------------------------------------------------------------------
 *红黑树与四阶B树(2-4树)的等价性
 * 红黑树的黑节点与它的红色子节点结合成一个B树子节点
 * 红黑树黑节点的数目就是对应b树节点的数目
 * -------------------------------------------------
 * 涉及到的概念
 * parent父节点
 * sibling 兄弟节点
 * uncle叔父节点
 * grand祖父节点
 * @param <E>
 */
public class RBTree<E> extends BBST<E> {
    public static final boolean RED=false;
    public static final boolean BLACK=true;
    public RBTree() {
        this(null);
    }
    public RBTree(Comparator<E> comparator) {
        super(comparator);
    }

    /**
     * 添加:如果第一次添加的是根节点,但是默认颜色为红色,所以需要将其染黑
     *
     *                          |-----红---黑---红
     *       |                   |                |                  |
     *  红---黑---红            黑--红           红--黑               黑
     *        4                  3               3                  2
     * 1.有四种情况直接满足红黑树的性质,父节点parent为黑节点--->不可能造成两个红节点连续的情况
     * 2.其余八种父节点(parent)需要特殊处理---又按照叔父节点是红色和叔父节点是黑色划分,叔父节点是红色的会发生上溢
     * 。但是叔父节点为黑色另有不同--->LL RR LR RL (grand进行单旋)
     * @param node 新添加的节点->太秒了
     */
    @Override
    protected void afterAdd(Node<E> node) {
        Node<E> parent=node.parent;
        //这个代码块包括两种情况--->(1)第一次添加节点(2)叔父节点是红色的情况下,上溢到了根节点
        if(parent==null){
            black(node);
            root=node;
            return;
        }
        if(isBlack(parent))
        {
            //排除本身就满足条件的哪四种情况,不需要额外处理
            return;
        }
        Node<E> uncle=parent.sibling();
        Node<E> grand=parent.parent;
        //叔父节点是红色-->发生上溢
        if(isRed(uncle)){
            //上溢分裂的情况
            black(parent);
            black(uncle);
            //把祖父节点当成一个新添加的节点加到上面-->解决上溢
            afterAdd(red(grand));
            return;
        }
        //叔父节点不是红节点
        if(parent.isLeftChild())
        {
            //由于染色顺序并不影响所以可以把统一的染色顺序放在前面red(grand)
            //LL RR 单旋
            if(node.isLeftChild()){//LL
                black(parent);
                red(grand);
            }else {//LR
                black(node);
                red(grand);
                rotateLeft(parent);
            }
            rotateRight(grand);
        }else{
            if(node.isRightChild())//RR
            {
                black(parent);
                red(grand);
            }else{//RL
                black(node);
                red(grand);
                rotateRight(parent);
            }
            rotateLeft(grand);
        }
    }
    /**
     * b树中最后被删除的一定都在叶子节点中
     * 删除红色节点--->不会影响红黑树的性质就直接进行删除就行
     *
     *
     */

    //删除黑色节点要传入黑色节点的替代节点
    @Override
    protected void afterRemove(Node<E> node, Node<E> replacement) {
        //b树类比红黑树一定是删除的最后一层。如果要删除的是红色,由于黑色是主体,
        // 所以要删除的节点一定度为0的红色叶子节点,不会影响原本红黑树的规则,所以不用调整,直接结束函数
        if(isRed(node)){
            return;
        }
        /**
         * 一定要清楚afterRemove的作用是删除之后的调整,不是主体删除方法。
         * 作用:对于不符合红黑树性质的操作及时进行调整
         *
         * 问:对于删除度为2的黑色节点,是不是可是囊括
         * 答:可以:因为删除度为2的黑色节点,找到前驱后,还是删除红色节点不用处理,染黑替代节点并不影响,因为已经删除
         *
         * 为什么黑色节点的替代不能为黑色节点
         * 因为b树的删除都是在最后一层进行操作,如果黑色节点子节点依然是一个黑色节点,由于黑色才是主体
         * 不会融入到一个节点中,所以只会向下增高一层,不符合删除在最后一层操作。,
         */
        if(isRed(replacement)){
            //染黑:避免出现连续两个红色节点
            black(replacement);
            return;
        }
        /**
         * 处理最后一种情况-->删除度为0的黑色叶子节点的调整方法
         *
         * 删除度为0的黑色叶子节点的调整方法肯定会造成下溢
         * 补救:(1)看看兄弟节点能不能借
         * a:兄弟节点能借的条件:1.兄弟节点是黑色 2.兄弟节点至少拥有一个红色子节点
         * ----------------------------------------
         * 能借后做出的操作:
         * 进行旋转操作
         * 旋转之后的中心节点继承 parent 的颜色
         * 旋转之后的左右节点染为 BLACK(因为此时红色子节点已经旋上去了)
         *
         * 旋转有三种情况:LL LR LL
         *
         * 🤩🤩特殊情况1:
         * 如果被删除的是黑色,它的父节点也是黑色,删除后黑色父节点也会向下合并,造成父节点继续下溢
         *只需将这个父节点当成一个新的被删除的黑色节点递归调用afterRemove方法,重复利用代码
         *
         * 🤩🤩特殊情况2:
         * 🚩🚩🚩如果兄弟节点是红色,对于b树的性质,【黑色节点与其红色节点组成一个b树节点】
         * 可知:红色节点一定在父节点中-->红色的节点的子节点一定是黑的,可以借侄子的【想办法将侄子变成兄弟】
         * 强制将侄子变成兄弟再次套用兄弟节点是黑色是的代码
         *-----------------------------------------------
         */

        Node<E> parent=node.parent;
        if(parent==null)
        {
            return;
        }
        //这么写有问题:node已经被删除--->指向node的线已经断了
        //Node<E> sibling =node.sibling();
        /**
         *  需要间接求出兄弟节点
         *  只需知道被删除的节点先前是在parent的左边还是右边,进而知道sibling是parent.left还是parent.right
         */

        /**
         *    为什么以此作为判定标准:
         *         因为在二叉搜索树的删除中,删除黑色叶子节点,已经将一边置为null
         *         if (node == node.parent.left) {
         *             node.parent.left=null;
         *         } else { // node == node.parent.right
         *             node.parent.right=null;
         *         }
         */
        //有特殊情况
        boolean left=parent.left==null||node.isLeftChild();
        Node<E> sibling=left?parent.right:parent.left;
        if(left){//node是在左子树,兄弟节点在右边

            if(isRed(sibling)){
                black(parent);
                red(sibling);
                rotateLeft(parent);
                //更新兄弟
                sibling=parent.right;
            }

            //兄弟节点是黑色-->判断是否有红色子节点

            //没有一个红色子节点--->父节点向下合并

            /**
             * 为什么以父节点是不是黑的为判定条件
             * 因为如果父节点是红色,那它肯定不是主体,向下合并后,parent本身并没有下溢
             * 如果父节点是黑色,那他向下合并后,自身也会下溢
             *
             */
            if(isBlack(sibling.left)&&isBlack(sibling.right)){
                boolean parentBlack=isBlack(parent);
                black(parent);
                red(sibling);
                //这种情况父节点向下合并,父节点的位置也会下溢
                if(parentBlack){
                    //三黑局面没有替代节点
                    afterRemove(parent,null);
                }

            }else {//至少有一个红色子节点
                //判断是LL LR.....
                //三种情况:LL LR (LL或LR)
                //可以先统一进行左旋转然后在统一进行右旋转
                //如何区分LR左子节点是黑的
                if(isBlack(sibling.left)) {//LR情况

                    rotateLeft(sibling);
                    //左旋转后兄弟节点已经发生了变化
                    sibling = parent.left;
                }
                //LL 情况
                //现在的sibling已经更新过是图中的78
                color(sibling,iscolor(parent));
                black(parent);
                black(sibling.left);
                rotateRight(parent);
            }

        }else {//node是在右子树,兄弟节点在左边
            //🤩🤩特殊情况2:
            if(isRed(sibling)){
                black(parent);
                red(sibling);
                rotateRight(parent);
                //更新兄弟
                sibling=parent.left;
            }

            //兄弟节点是黑色-->判断是否有红色子节点

            //没有一个红色子节点--->父节点向下合并

            /**
             * 为什么以父节点是不是黑的为判定条件
             * 因为如果父节点是红色,那它肯定不是主体,向下合并后,parent本身并没有下溢
             * 如果父节点是黑色,那他向下合并后,自身也会下溢
             *
             */
            if(isBlack(sibling.left)&&isBlack(sibling.right)){
                boolean parentBlack=isBlack(parent);
                black(parent);
                red(sibling);
                //这种情况父节点向下合并,父节点的位置也会下溢
                if(parentBlack){
                    //三黑局面没有替代节点
                    afterRemove(parent,null);
                }

            }else {//至少有一个红色子节点
                //判断是LL LR.....
                //三种情况:LL LR (LL或LR)
                //可以先统一进行左旋转然后在统一进行右旋转
                //如何区分LR左子节点是黑的
                if(isBlack(sibling.left)) {//LR情况

                    rotateLeft(sibling);
                    //左旋转后兄弟节点已经发生了变化
                    sibling = parent.left;
                }
                //LL 情况
                //现在的sibling已经更新过是图中的78
                color(sibling,iscolor(parent));
                black(parent);
                black(sibling.left);
                rotateRight(parent);
            }

        }

    }

    @Override
    protected Node<E> createNode(E element, Node<E> parent) {
        return new RBNode<>(element,parent);
    }

    //染色--->好处parent=color(node.parent,color)--->染色的同时还能更新node的parent
private Node<E> color(Node<E> node,boolean color){
        //代码习惯:染色的同时返回被染色的节点便于对其进行一些操作
    if(node==null)
    {
        return null;
    }
    ((RBNode<E>)node).color=color;
    return node;
}
public Node<E> red(Node<E> node)
{
    return color(node,RED);
}
public Node<E> black(Node<E> node)
{
    return color(node,BLACK);
}

//判断颜色
    private boolean iscolor(Node<E> node)
    {
        return node==null?BLACK:((RBNode<E>)node).color;

    }
    public boolean isRed(Node<E> node)
    {
        return iscolor(node)==RED;

    }
    public boolean isBlack(Node<E> node){
        return iscolor(node)==BLACK;
    }
    //自定义内部类-->红黑树类型
    public static class RBNode<E>extends Node<E>{
        boolean color=RED;
        //为什么默认为红色:因为默认为红色的情况下对于红黑树的五个条件除了第四个条件都满足,更加的适合进行默认颜色
        //更容易使红黑树的性质得到满足
        //4不满足的可能情况使出现两个连续的红色子节点
        public RBNode(E element, Node<E> parent) {
            super(element, parent);
        }

        //内部类重新定义一个方法--->返回兄弟节点
        //不用定义返回叔父节点-->因为node.parent.sibling()-->就是叔父节点
        public Node<E> sibling()
        {
            if(isLeftChild())
            {
                return parent.right;
            }
            if(isRightChild()){
                return parent.left;
            }
            return null;
        }

        @Override
        public String toString() {
           String str="";
           if(color==RED){
               str="R_";
           }
          return str+element.toString();

        }
    }


}

在这里插入图片描述

相关文章:

  • [Power Query] 分组依据
  • Scala系列从入门到精通(三)
  • 项目框架:登录跳转页面
  • 【毕业设计】视频多目标跟踪系统 - 深度学习 机器视觉
  • Deformable detr源码分析
  • 阿里巴巴Java方向面试题汇总(含答案)
  • (利用IDEA+Maven)定制属于自己的jar包
  • OpenCV dnn模块 分类模型Resnet50 OpenCV dnn模块部署 .onnx模型
  • MySQL入门 - 数据分组之 group by
  • 拼多多分类ID搜索商品数据分析接口(商品列表数据,商品销量数据,商品详情数据)代码对接教程
  • CEO问CIO:数字化运营到底要解决什么问题?
  • 3.16 haas506 2.0开发教程-example-JC035串口屏
  • DPDK的VFIO
  • 重要?2022年第二批四川省工程技术研究中心组织申报条件、时间、奖励及流程
  • 【老王读Spring Transaction-1】从EnableTransactionManagement顺藤摸瓜,研究@Transactional的实现原理
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • conda常用的命令
  • Java 内存分配及垃圾回收机制初探
  • javascript数组去重/查找/插入/删除
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • Objective-C 中关联引用的概念
  • Otto开发初探——微服务依赖管理新利器
  • React-Native - 收藏集 - 掘金
  • SAP云平台运行环境Cloud Foundry和Neo的区别
  • UEditor初始化失败(实例已存在,但视图未渲染出来,单页化)
  • 成为一名优秀的Developer的书单
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 让你的分享飞起来——极光推出社会化分享组件
  • 我看到的前端
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 一个普通的 5 年iOS开发者的自我总结,以及5年开发经历和感想!
  • 一天一个设计模式之JS实现——适配器模式
  • 因为阿里,他们成了“杭漂”
  • 原生 js 实现移动端 Touch 滑动反弹
  • 阿里云服务器购买完整流程
  • 我们雇佣了一只大猴子...
  • ​ssh免密码登录设置及问题总结
  • #{} 和 ${}区别
  • #每天一道面试题# 什么是MySQL的回表查询
  • (python)数据结构---字典
  • (十三)Maven插件解析运行机制
  • (四)linux文件内容查看
  • (太强大了) - Linux 性能监控、测试、优化工具
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • (转)Mysql的优化设置
  • (转)程序员疫苗:代码注入
  • (轉貼) UML中文FAQ (OO) (UML)
  • ****** 二十三 ******、软设笔记【数据库】-数据操作-常用关系操作、关系运算
  • .NET 中让 Task 支持带超时的异步等待
  • .NET项目中存在多个web.config文件时的加载顺序
  • @Transactional注解下,循环取序列的值,但得到的值都相同的问题
  • [ 环境搭建篇 ] 安装 java 环境并配置环境变量(附 JDK1.8 安装包)
  • [20190113]四校联考
  • [Bada开发]初步入口函数介绍
  • [C#]猫叫人醒老鼠跑 C#的委托及事件