专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

读书笔记-红黑树

前言

大家好,头回写博客,欢迎批评,以后我会尽量做到一个月2更,最近在重新温故算法。 今日提供读书笔记红黑树

目的

记录所学,温故知新

Java中对应的结构

TreeMap,以下是自己安装书中实现的原理,工作中应使用TreeMap

红黑树的定义

红黑树(Red Black Tree) 是一种自平衡二叉查找树.
红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡

时间界限与特点

红黑树的插入,删除操作在最坏情况下花费

log N

红黑树是具有如下着色性质的二叉查找树:

1、 每个节点要么着成红色,要么着成黑色
2、 根是黑色的
3、 如果一个节点是红色,那么他的子节点必须是黑色
4、 从一个节点到一个null引用,每一条路径必须包含相同数目的黑色节点。

使用该着色法则,保证红黑树的高度最多为:

2*log (N+1)

插入操作

自底向上插入及遇到的问题

自底向上插入

如果新插入的项的父节点是黑色,那么插入结束,默认新插入的节点是红色的.

如果父亲是红色的,就有几种情形(每种都有对称形式,假设父亲是曾祖父的左儿子).

  • 父亲的兄弟是黑色,

如下图:

68_1.png 父亲的兄弟是黑色的调整示意图

    • 曾祖父为黑色,当前节点为父亲的右儿子且为红色.

68_2.png 父亲的兄弟是黑色并且父亲的右儿子为红色

  • 父亲的兄弟是红色时, 需要进行上滤 如下图过程:

68_3.png 进行上滤的示例

遇到的问题

上滤需要一个栈或者保持父链来实现,并且过程复杂.

自顶向下插入

概念:在向下的过程中如果看到一个节点current有两个红儿子,可将该节点呈红色,两个儿子变为黑色。

68_4.png 红黑树颜色翻转

当current节点的父亲parent也是红色时候,进行适当的选择,以该方式向下进行插入操作屏蔽了X节点的兄弟节点也是红色的可能. 代码:

/**
     * 自顶向下插入
     */
    public void insert( AnyType item ){
          nullNode.element=item;
          current=parent=grand=header;
          //自顶向下调整,避免插入的父节点和父节点的兄弟节点为黑色的情况,该情况复杂不利于恢复平衡信心.
          while(compare(item,current)!=0){
              great=grand;
              grand=parent;
              parent=current;
              current=compare(item,current)<0?current.left:current.right;
              if(current.left.color==RED&&current.right.color==RED){
                  handleReorientAfterInsert(item);
              }
          }

          if(current!=nullNode){//重复元素跳过
              return;
          }
          //找到位置
          //构建新的节点
          current=new RedBlackNode<AnyType>(item,nullNode,nullNode);
          //维护与父节点的关系
          if(compare(item,parent)<0){
              parent.left=current;
          }else{
              parent.right=current;
          }
          //插入完成后,维护平衡信息
          handleReorientAfterInsert(item);
          nullNode.element=null;
    }
/**
     * 插入后维护平衡信息
     * @param item
     */
    private void handleReorientAfterInsert(AnyType item) {
        //初步调整的变换颜色 自己变为红色,两个儿子变为红色
        current.color=RED;
        current.left.color=BLACK;
        current.right.color=BLACK;
        if(parent.color==RED){
            //调整后破坏了红黑树性质,需要旋转
            //分两种类型 一字形和之字形,之字形比一字形调整了多一步
            grand.color = RED;
            if((compare(item,grand)<0)!=(compare(item,parent)<0)){//之字形
                parent=rotate(item,grand);
                //调整parent和他的儿子,并将调整后的节点W设置成parent
            }
            //调整完成,重新设置当前节点
            current=rotate(item,great);
            //并将当前节点设置为黑色
            current.color=BLACK;
        }
        //保证根节点是黑色
        header.right.color=BLACK;
    }

自顶向下删除

  • 删除操作归结于可以删除红色的树叶;
  • 如果要删除的节点有右儿子,以右儿子的最小元内容替换要删除节点内容,之后删除右儿子最小元来进行删除。
  • 如果只有左儿子,以左儿子最大元内容替换要删除节点的内容,之后删除左儿子最大元
  • 如果要删除的节点没有儿子, 将该节点调整成红色,将父节点对应的引用设置成nullNode
  • 3.如果没有儿子
    • 若父节点为header,将树变为空树
    • 否则如果当前节点为黑色,进行调整,保证删除项为红色,之后将要删除项的父节点的引用设置为nullNode.

红色树叶删除简单,如果要删除的是黑色分为如下几种情:

1、 X与兄弟T的儿子都是黑色

![68\_5.png][68_5.png] X与兄弟T的儿子都是黑色

2、 X的儿子是黑色,兄弟T有一个左儿子是红色

![68\_6.png][68_6.png] X的儿子是黑色,兄弟T有一个左儿子是红色

3、 X的儿子是黑色,兄弟T有一个右儿子是红色

![68\_7.png][68_7.png] X的儿子是黑色,兄弟T有一个右儿子是红色

4、 X的儿子是黑色,兄弟T儿子都是红色

![68\_8.png][68_8.png] X的儿子是黑色,兄弟T儿子都是红色

以上每种情形都有与只对应的对称类型。如果X节点是红色,我们生产新的X,P,T向下探索 相关代码:

    /**
     * 删除一个节点,
     * 依据可以删除一个叶子,
     * 自顶向下删除,
     * 1如果要删除项有右儿子,先删除右儿子最小项,之后使用原右儿子的最小项内容替换要删除项的内容.
     * 2.如果只有左儿子,先删除左儿子最大,之后使用左儿子的最大项替换要删除项的内容.
     * 3.如果没有儿子
     *  若父节点为header,将树变为空树
     *  否则如果当前节点为黑色,进行调整,保证删除项为红色,之后将要删除项的父节点的引用设置为nullNode.
     * @param x
     */
    public AnyType remove( AnyType x ){
        //需要自己尝试书写
        //先查找是否存在,存在后删除
         RedBlackNode<AnyType>p=find(x);
         RedBlackNode<AnyType>pParent=parent;
         if (p == null){
            return null;
         }
         AnyType item=p.element;
         //自顶向下删除
         //找到后,如果存在左儿子和右儿子(或 只有右儿子),
         //使用右儿子的最小,替换当前 ,之后删除右儿子最小
         //只有左儿子使用左儿子最大替换,
         RedBlackNode<AnyType>replacement=findReplaceMent(p);
         if(replacement!=null){
             //进行替换
             p.element=remove(replacement.element);

         }else{
             //没有替换者,
             if(pParent==header){
                makeEmpty();
             }else{
                 if(p.color==BLACK){
                     //将p地调整为红色
                     fixbeforedelete(p.element) ;
                     pParent=parent;
                 }
                //调整为删除
                 if(pParent.left==p){
                    pParent.left=nullNode;
                  }else if(pParent.right==p){
                    pParent.right=nullNode;
                  }

             }
         }
         current=p;
         parent=pParent;
         return item;
    }

    /**
     * 删除前调整数的平衡信息,保证要删除的项是红色
     * @param item
     */
    private void fixbeforedelete(AnyType item) {
        grand=header;
        RedBlackNode<AnyType>p=header.right;
        RedBlackNode<AnyType>x=nullNode;
        RedBlackNode<AnyType>t=nullNode;
        RedBlackNode<AnyType>i=find(item);
        //先把p涂成红色,最后恢复
        p.color=RED;
        x=item.compareTo(p.element)<=0?p.left:p.right;
        t=item.compareTo(p.element)<=0?p.right:p.left;
        //保证要删除的项是红色
        while(i.color!=RED){
            if(x.color==RED||
                (x.color==BLACK&&(x.left.color==RED&&x.right.color==RED)||
                t.color==BLACK&&(x.left.color==RED||x.right.color==RED))    
            ){
                //x为红色或x儿子为红色,x为黑色&&t为黑色,x有一个儿子为红色,向下探索
                grand=p;
                p=x;
                x=item.compareTo(p.element)<0?p.left:p.right;
                t=item.compareTo(p.element)<0?p.right:p.left;
            }else if(x.color==BLACK&&t.color==BLACK
                    &&x.right.color==BLACK&&x.left.color==BLACK){
                //3中情况需要,调整的情况
                if(t.left.color==BLACK&&t.right.color==BLACK){
                    //t的两个儿子,直接变换p和t,x的颜色,重新再该位置下探
                    p.color=BLACK;
                    t.color=RED;
                    x.color=RED;
                }else if(t.left.color==RED&&t.right.color==RED){
                    //t有两个红色的儿子,调整后下探
                    if(p.right==t){
                        RedBlackNode<AnyType>red=t.left;
                        p.right=red.left;
                        t.left=red.right;
                        red.right=t;
                        red.left=p; 
                        //更新祖父节点
                        if(grand.left==p){
                            grand.left=red;
                        }else{
                            grand.right=red;
                        }
                        grand=red;
                        p.color=BLACK;
                        x.color=RED;
                        t=p.right;  
                    }else{
                        RedBlackNode<AnyType>red=t.right;
                        p.left=red.right;
                        t.right=red.left;
                        red.right=p;
                        red.left=t;
                        if(grand.left==p){
                            grand.left=red;
                        }else{
                            grand.right=red;
                        }
                        grand=red;
                        p.color=BLACK;
                        x.color=RED;
                        t=p.left;
                    }
                }else if(p.right==t&&t.left.color==RED){
                    //右左,之字调整后继续下探
                    RedBlackNode<AnyType>red=t.left;
                    p.right=red.left;
                    t.left=red.right;
                    red.right=t;
                    red.left=p; 
                    if(grand.left==p){
                        grand.left=red;
                    }else{
                        grand.right=red;
                    }
                    grand=red;
                    p.color=BLACK;
                    x.color=RED;
                    t=p.right;
                }else if(p.left==t&&(t.right.color==RED)){
                    //左右,之字调整后继续下探
                    RedBlackNode<AnyType>red=t.right;
                    p.left=red.right;
                    t.right=red.left;
                    red.right=p;
                    red.left=t;
                    if(grand.left==p){
                        grand.left=red;
                    }else{
                        grand.right=red;
                    }
                    grand=red;
                    p.color=BLACK;
                    x.color=RED;
                    t=p.left;
                }else if(p.right==t&&t.right.color==RED){
                    //右右 一字,交换t和p
                    p.right=t.left;
                    t.left=p;
                    if(grand.left==p){
                        grand.left=t;
                    }else{
                        grand.right=t;
                    }
                    grand=t;
                    t.color=RED;
                    p.color=BLACK;
                    t=p.right;
                }else if(p.left==t&&t.left.color==RED){
                    //左左 一字 交换t和p
                    p.left=t.right;
                    t.right=p;
                    if(grand.left==p){
                        grand.left=t;
                    }else{
                        grand.right=t;
                    }
                    grand=t;
                    t.color=RED;
                    p.color=BLACK;
                    t=p.left;
                }
        }else if(x.color==BLACK&&p.color==BLACK&&t.color==RED){
            //x的兄弟为黑色,x和x的父节点都是红色,调整t和p,保证p为红色后,继续下探
            if(p.left==x){
                p.right=t.left;
                t.left=p;
                if(grand.left==p){
                    grand.left=t;
                }else{
                    grand.right=t;
                }
                grand=t;
                t.color=BLACK;
                p.color=RED;
                t=p.right;
            }else{
                p.left=t.right;
                t.right=p;
                if(grand.left==p){
                    grand.left=t;
                }else{
                    grand.right=t;
                }
                grand=t;
                t.color=BLACK;
                p.color=RED;
                t=p.left;
            }
        }else if(header.right==p&&x.color==BLACK
                &&p.color==RED&&t.color==RED){
            p.color=BLACK;
        }
    }
        header.right.color=BLACK;
        parent=p;
}

总结

1、 红黑树的操作在最坏情况下花费

log N

1、 插入操作采用自顶向下操作保证要插入的节点的父亲是黑色。
2、 删除操作采用自顶向下操作保证要删除的节点为红色。

红黑树完整代码地址

github.com/floor07/Dat…

文章永久链接:https://tech.souyunku.com/35842

未经允许不得转载:搜云库技术团队 » 读书笔记-红黑树

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们