数据结构之红黑树完整代码实现

上一篇: B+树及其实现
下一篇: 二叉树及其实现

一、什么是红黑树

红黑树是一种弱平衡二叉树,它拥有二叉树的基本性质:任意一个节点的值大于左节点的值小于右子节点的值。和AVL一样都是在插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。 红黑树查找的最坏时间复杂度也是O(logN)。

二、红黑树的性质

(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点(NIL–虚拟节点,节点为null的节点)是黑色.
(4) 每个红色节点的两个子节点一定是黑色,不能有2个相连的红色节点
(5) 任意一个节点到每个叶子节点即NIl节点的路径都包含相同数量的黑色节点。–体现黑色完美树–俗称黑高。
性质5扩展 :如果一个节点存在黑子节点,那么该节点肯定有两个子节点。

三、红黑树的旋转操作

1、左旋
某个节点作为旋转点,其右子节点变为旋转节点的父节点,右子节点的左子节点为旋转节点的右子节点,左子节点保持不变。----基于最短路径及性质5可以确定旋转方向。
 一张动图解决左旋

 /**
     * 左旋
     *            p                               p
     *            |                               |
     *            x                               y
     *           / \      --(左旋)->             / \
     *          lx  y                          x   ry
     *             / \                        / \
     *            ly  ry                     lx  ly
     * @param x
     */
    private void leftRotate(TNode x){
        //找到x的右节点
        TNode y=x.right;
         x.right=y.left;//将x的右子节点指向y的左子节点ly
         if(null!=y.left){
             y.left.parent=x;//将y的左子节点的父节点更新为x
         }
         if(null!=x.parent){//当x的父节点不为空时
             y.parent=x.parent;//更新y的父节点为x的父节点
             if(x.parent.left==x){
                 x.parent.left=y;//则将y设为x的父节点的左节点
             }else {
                 x.parent.right=y;
             }

         }else {
             root=y;//当x的父节点为空时,y节点作为根节点
             root.parent=null;//保证y的父节点不存在
         }


        x.parent=y;//将x的父节点指向y
        y.left=x;//将y的左节点的更新为x
    }

2、右旋
某个节点作为旋转点,其左子节点变为旋转节点的父节点,左子节点的右子节点为旋转节点的左子节点,右子节点保持不变。----基于最短路径及性质5可以确定旋转方向。
一张动图搞定右旋

/**
     * 右旋
     *          p                               p
     *          |                               |
     *          y         --(右旋)->            x
     *         / \                            /  \
     *        x  ry                          lx   y
     *       / \                                 / \
     *      lx rx                               rx  ry
     * @param y
     */
    private  void  rightRotate(TNode y){
        TNode x=y.left;//第一步获取 y节点的左节点x
        if(null!=x.right){
            x.right.parent=y;//第二步将x的右节点的父节点更新为y,此时x的右节点的指向也需要更改
        }
        y.left=x.right;//将y的左节点指向为x的右节点rx
        if(null!=y.parent){ //第三步  y的父节点如果不为空则更新为x的父节点为y的父节点
            x.parent=y.parent;
            if(y.parent.left==y){
                y.parent.left=x;
            }else {
                y.parent.right=x;
            }
        }else {
            root=x;//当y的父节点为空时,x节点作为根节点
            root.parent=null;
        }

        y.parent=x;//第四步将y的父节点更新为x;x的右节点指向y
        x.right=y;
    }

四、红黑树的查找

和二叉树查找一样,更据key值从根节点开始查找,比根大从右边查找,比根小从左边查找

 private TNode searchNode(TNode node, K key){
        if(null==node){
            return  null;
        }
        int cmp= node.key.compareTo(key);
        if(cmp<0){
            return searchNode(node.right,key);
        }
        else if(cmp>0){
            return searchNode(node.left,key);
        }
        else{
            return node;
        }
    }

五、红黑树的插入

红黑树插入节点默认都是红色节点。why?-beacuse:插入红色节点可以尽可能的减少红黑树的平衡修复

插入的情景

A:红黑树是空树即父节点为null,直接插入变色为黑色即可
B:插入的节点key值已经存在,更新当前节点值为插入节点值
C:插入节点的父节点为黑节点 ,没有变化 ,红黑树不需要处理
D:插入节点的父节点为红色节点,插入节点存在祖父节点且必须为黑色(性质4), 很关键旋转操作需要祖父节点参与,此时不满足性质4需要自平衡操作
D1:叔叔节点(U)存在且为红色节点:当前节点C,父节点P
1 将P 和U节点变为黑色
2 将G节点改为红色
3 将G节点设置为当前节点,将G节点最为新的操作节点进行递归操作

D2:叔叔节点(U)不存在或者为黑色节点,且插入节点的父节点P是祖父节点(G)的左子节点
D2-1:新插入节点为其父节点的左子节点

1先变色 P设置为黑色
2G设置为红色
3对G节点右旋操作

D2-2:新插入节点为其父节点的右子节点
1 P左旋
2 P设置为当前节点
3 对P递归 按照D2-1处理

D3:叔叔节点(U)不存在或者为黑色节点,且插入节点的父节点P是祖父节点(G)的右子节点
D3-1 新插入节点为其父节点的右节点

1 P设置为黑色
2 G设置为红色
3 对G节点左旋操作

D3-2:新插入节点为其父节点的左子节点

1 P右旋
2 P设置为当前节点
3 对P递归 ,按照D3-1处理

插入流程图

在这里插入图片描述

//插入node节点
private  void  insertNode(TNode node){
    
        if(node==null){
            return;
        };
        TNode parent=null;
        //找node节点的父节点
        parent=searchParent(node,root);
        node.parent=parent;
        if(node.parent!=null){//插入父节点的左树还是右树
            int cmp= node.key.compareTo(node.parent.key);
            if(cmp<0){
                node.parent.left=node;
            }
            else {
                node.parent.right=node;
            }
        }else {
            root=node;
        }
        insertFlxUp(node);//平衡修复
    }
//插入平衡修复
private void  insertFlxUp(TNode node){
          TNode nodeP=node.getParent();
          root.setColor(BLACK);//根节点设为黑色
           //插入的父节点为红色
          if(null!=nodeP&&isRed(nodeP)){
              TNode gNode=nodeP.parent;//爷爷节点
              if(null!=gNode){
                  TNode uncleNode=unlceOf(node);//叔叔节点存在且是红色
                  if(null!=uncleNode&&isRed(uncleNode)){
                      setBlack(uncleNode);//叔叔节点染黑
                      setBlack(nodeP);//父节点染黑
                      setRed(gNode);//爷爷节点染红
                      insertFlxUp(gNode);//爷爷节点设置为当前节点--以爷爷节点进行递归
                      return;
                  }
                  if(nodeP==gNode.left){ //叔叔节点(U)不存在或者为黑色节点,且插入节点的父节点P是祖父节点(G)的左子节点
                      if(node==nodeP.right){//新插入节点为其父节点的右子节点
                          leftRotate(nodeP);//对P进行左旋;然后指定父节点为当前操作进行下一轮递归处理
                          insertFlxUp(nodeP);
                          return;
                      }
                      //新插入节点为其父节点的左子节点
                      setBlack(nodeP);
                      setRed(gNode);
                      rightRotate(gNode);
                  } else{//叔叔节点(U)不存在或者为黑色节点,且插入节点的父节点P是祖父节点(G)的右子节点

                       if(node==nodeP.left){//新插入节点为其父节点的左节点
                            rightRotate(nodeP);
                            insertFlxUp(nodeP);
                            return;
                       }
                       //新插入节点为其父节点的右子节点
                      setBlack(nodeP);
                      setRed(gNode);
                      leftRotate(gNode);
                  }
              }
          }
    }

六、 红黑树的删除

红黑树的删除要比插入稍微复杂点,其实掌握本质还是比较简单的;
删除不是黑色节点就是红色节点;删除节点没有子节点或者有子节点:1个或者2个。有1个子节点找孩子节点替换当前删除节点;2个子节点找后继或者前驱节点替换删除节点(替换时保留当前被删除节点保留指针和颜色)
删除场景:
A 如果删除的节点是叶子节点(没有子节点),那么又分为两种子情况
A1 被删除的节点是红色,删除后无需调整
A2 被删除节点是黑色,删除后需要调整
B 如果删除的节点只有一个子节点则删除节点只能是黑色,子节点比为为红色–否则无法满足红黑树的性质5。 此时用删除节点的子节点接到父节点,且将子节点颜色涂黑,保证黑色数量
C 如果删除的节点有两个子节点—>找被删除节点的前驱或者后继作为替换节点
替换节点只能是
C1: 替换节点N是叶子节点 —> 对应情况 A1 或 A2
C2: 替换节点N有一个子节点 ----> 对应情况 B

后继节点和前驱节点怎么找呢?
前驱节点
若一个节点有左子树,那么该节点的前驱节点是其左子树中值最大的节点(左子树最右边的叶子节点)
若一个节点没有左子树,那么判断该节点和其父节点的关系
若该节点是其父节点的右边孩子
若该节点是其父节点的左边孩子那么需要沿着其父亲节点一直向树的顶端寻找,直到找到一个节点P,P节点是其父节点Q的右边孩子,那么Q就是该节点的前驱节点
后继节点
若一个节点有右子树,那么该节点的后继节点是其右子树中val值最小的节点(右子树最左边的叶子节点)
若一个节点没有右子树,那么判断该节点和其父节点的关系
若该节点是其父节点的左边孩子,那么该节点的后继结点即为其父节点
若该节点是其父节点的右边孩子 那么需要沿着其父亲节点一直向树的顶端寻找,直到找到一个节点P,P节点是其父节点Q的左边孩子,那么Q就是该节点的后继节点

删除流程图:
在这里插入图片描述

//后继节点
private TNode successor(TNode node){
        if(null!=node){
            TNode nodeR=node.right;
            //获取node存在右子树则从右子树中找最左叶子节点
           if(nodeR!=null){
               while (null!=nodeR.left){
                   nodeR=nodeR.left;

               }
               return  nodeR;
           } else{//没有右节点则判断该节点和其父节点的关系
               TNode p=parentOf(node);//如果是左孩子父节点即为后继节点
               //判断node节点是其父节点的右孩子,向上找找到一个节点P是其父节点Q的左子节点则Q即为后继节点
               while (null!=p&&node==p.right){
                   node=p;
                   p=p.parent;

               }
               return  p;
           }
        }
        return  null;
    }
    
//前驱节点
private TNode previousSuccessor(TNode node){
        if(null!=node){
             TNode nodeL=node.left;
            if(null!=nodeL){
                while (null!=nodeL.right){
                    nodeL=nodeL.right;
                }
                return  nodeL;
            }else{
                TNode nodeP=parentOf(node);
                while (null!=nodeP&&node==nodeP.left){
                    node=nodeP;
                    nodeP=nodeP.parent;
                }
                return  nodeP;
            }
        }
        return null;
    }

三 删除修复
A:被删除的替换节点x是P的左节点

情况一:被删除x是黑色节点,它的兄弟节点(W)是红色节点
1将x的兄弟节点w染成黑色
2 x的父节点p染成红色
3 p节点进行左旋
变成: 被删除节点x是黑色,x的兄弟节点w也是黑色,并且w的子节点都是黑色–拿到处理后的兄弟节点进入情况二:兄黑
情况二: 被删除节点是黑色,它的兄弟节点是黑色,兄弟节点的子节点都是黑色
分类1:如果x的父节点p是红色节点,那么将兄弟节点的颜色和父节点的颜色互换,即w染成红色,父节点p染成黑色就完成了修复
分类2:如果x的父节点p是黑色,那么就将兄弟节点w染成红色,现在x和w路径上都少了一个黑色节点,所以将p看成一个新的x即将p当成待处理节点,进行递归
情况三:被删除节点X是黑色,它的兄弟节点W是黑色,w的右子节点是黑色(黑色的方向和兄弟节点的方向共线)
1 w和它的左子节点交换颜色 即w染成红色,w的左子节点染成黑色
2 对W进行右旋
拿到处理后的兄弟节点进入情况四
情况四:被删除节点X是黑色,它的兄弟w是黑色节点,右子节点是红色 (红色节点的方向和自己的方向共线(和情况3是镜像))
1 将w节点的颜色和其父节点p节点的颜色互换
2 对p节点进行左旋
4 将w的右子节点染成黑色

B:被删除的替换节点x是P的右节点----与A相反

情况一:被删除的替换节点x是黑色节点,它的兄弟节点(W)是红色节点 1 兄弟节点与其父节点交换颜色
即将x的兄弟节点w染成黑色x的父节点p染成红色
2 P节点进行右旋
变成:被删除节点x是黑色,x的兄弟节点w也是黑色,并且w的子节点都是黑色(空节点也是黑色)–拿到处理后的兄弟节点进入情况二:兄黑
情况二: 被删除节点x是黑色,它的兄弟节点是黑色,兄弟节点的子节点也是黑色
分类1:如果x的父节点p是红色节点,那么将兄弟节点的颜色和父节点的颜色互换,即w染成红色,父节点p染成黑色就完成了修复
分类2:如果x的父节点p是黑色,那么就将兄弟节点w染成红色,将p看成一个新的x即将p当成待处理节点,进行递归
情况三:被删除节点X是黑色,它的兄弟节点W是黑色,左子节点是黑色(黑色的方向和兄弟节点的方向共线)
1 w和它的右子节点交换颜色 即w染成红色,w的右子节点染成黑色
2 对W进行左旋
拿到处理后的兄弟节点 进入情况四
情况四:被删除节点X是黑色,它的兄弟w是黑色节点,w的左子节点是红色(红色节点的方向和自己的方向共线(和情况3是镜像))
1 将w节点的颜色和其父节点p节点的颜色互换
2 对p节点进行右旋
3 将w的左子节点染成黑色

还有一种平衡修复方案(本人没试验过)

1:当前节点或者替换节点(D)是左节点:
①:兄弟节点(S)是黑色,且有且只有一个右节点(可断定右节点必定是红色)
1:父节点§颜色赋给兄弟节点,
2:将父节点§和兄弟节点(S)的右节点设置为黑色
3:对父节点§左旋
②:兄弟节点(S)是黑色,且有且只有一个左节点(可断定左节点必定是红色)
1:将左节点设置为黑色,
2:将兄弟节点(S)设置为红色
3:对兄弟节点点(S)右旋
③:兄弟节点(S)是黑色,有两个节点(可断定左右节点必定是红色)–情况同①
操作:1:父节点§颜色赋给兄弟节点,
2:将父节点§和兄弟节点(S)的右节点设置为黑色
3:对父节点§左旋
④:兄弟节点(S)是黑色,没有子节点
操作:1:兄弟节点(S)设置为红色,
2:将父节点§设置为当前节点进行递归,直到根节点或者遇到红色节点
⑤:兄弟节点(S)是红色,根据性质五得出必定有两个黑色子节点
操作:1:兄弟节点(S)设置为黑色,
2:兄弟节点(S)的左节点设置为红色,
3:将父节点§ 进行左旋
2:当前节点或者替换节点(D)是右节点
①:兄弟节点(S)是黑色,且有且只有一个左节点(可断定左节点必定是红色)
操作: 1:父节点§颜色赋给兄弟节点,
2:将父节点§和兄弟节点(S)的左节点设置为黑色
3:对父节点§右旋
②:兄弟节点(S)是黑色,且有且只有一个右节点(可断定右节点必定是红色)
操作:1:将右节点设置为黑色,
2:将兄弟节点(S)设置为红色
3:对兄弟节点点(S)左旋
③:兄弟节点(S)是黑色,有两个节点(可断定左右节点必定是红色)–情况同①
操作: 1:父节点§颜色赋给兄弟节点,
2:将父节点§和兄弟节点(S)的左节点设置为黑色
3:对父节点§右旋
④:兄弟节点(S)是黑色,没有子节点
操作:1:兄弟节点(S)设置为红色,
2:将父节点§设置为当前节点进行递归,直到根节点或者遇到红色节点
⑤:兄弟节点(S)是红色,根据性质五得出必定有两个黑色子节点
操作:1:兄弟节点(S)设置为黑色,
2:兄弟节点(S)的右节点设置为红色,
3:将父节点§ 进行右旋


 // 删除node节点公共方法
 public K removeNode(K key){
        if(null==key){
            return null;
        }
        removeNode(searchNode(root,key));
        return key;
    }
    
 private  void removeNode(TNode<K,V> deleteNode){

        if(deleteNode==null){
            return ;
        }
        TNode<K, V> successorsNode;

        if (deleteNode.left!=null&&deleteNode.right != null){
            successorsNode = successor(deleteNode);
            deleteNode.key=successorsNode.key;
            deleteNode.value=successorsNode.value;
        }
        else successorsNode=deleteNode;

        TNode<K, V> replaceNode=successorsNode.left!=null?successorsNode.left:successorsNode.right;
        TNode<K, V>  parentNode = successorsNode.parent;
        if (parentNode==null){
            root=replaceNode;
            if (replaceNode!=null) {
                replaceNode.parent=null;
                root.setColor(BLACK);
            }
        }
        else {

            if (replaceNode!=null){ //后继有替换节点则删除后继将替换节点连接后继的父节点,并染黑
                replaceNode.parent = parentNode;
                replaceNode.setColor(BLACK);
            }
            else {//没有替换节点直接删除后继节点,如果是黑色则先修复,然后删除
                if (isBlack(successorsNode)){
                    removeFlxUp(successorsNode);
                }
            }

            if (parentNode.left==successorsNode){
                parentNode.left=replaceNode;
            }
            else parentNode.right = replaceNode;

            successorsNode.left=successorsNode.right=successorsNode.parent=null;
        }

    }
//获取node叔叔节点
    private TNode unlceOf(TNode node){
        if(null!=node){
             TNode parent=parentOf(node);
             TNode gParent=parentOf(parentOf(node));
              if(parent!=null&&gParent!=null){
                   if(parent==gParent.left){
                       return gParent.right;
                   }
                   return gParent.left;
              }

        }
        return  null;
    }
    
 //平衡修复
 private void removeFlxUp(TNode node){
        if(null!=node&&isBlack(node)){
             if(node.parent!=null){
                 if(node==node.parent.left){
                     removeLeftFlxUp(node);
                 }
                 else{
                     removeRightFlxUp(node);
                 }
             }

        }
    }

//修复删除节点是其父节点的左子节点情况
 private void removeLeftFlxUp(TNode node){
        TNode nodeP=node.parent;
        TNode subNode=nodeP.right;//删除节点是左节点兄弟节点必然是右节点
        if(null!=subNode&&isRed(subNode)){//情况一:兄弟节点为红色
            swapNodeColor(nodeP,subNode);//兄弟节点与其父节点交换颜色
            leftRotate(nodeP);//对父节点左旋
            subNode=node.parent.right;//重新拿到兄弟节点进入情况二
        }
        //情况二:兄弟节点为黑色其两个字节点为黑色注意这里兄弟节点的孩子节点可能为空
        if((null==subNode.left||isBlack(subNode.left))&&(null== subNode.right||isBlack(subNode.right))){
             if(isRed(nodeP)){// 分类1:如果x的父节点p是红色节点,那么将兄弟节点的颜色和父节点的颜色互换,即兄弟染成红色,父节点p染成黑色就完成了修复
                 swapNodeColor(nodeP,subNode);
                 return;
             }
            //分类2:如果x的父节点p是黑色,那么就将兄弟节点w染成红色,所以将p看成一个新的x即将p当成待处理节点,进行递归
             setRed(subNode);
             removeFlxUp(nodeP);
             return;
        }else{
            //情况三:它的兄弟节点W是黑色,w的右子节点是黑色可能存在为空的需要处理
             if(null==subNode.right||isBlack(subNode.right)){
                  swapNodeColor(subNode,subNode.left);//兄弟节点与其左节点交换颜色
                  rightRotate(subNode);// 对兄弟节点进行右旋
                  subNode=nodeP.right;//拿到最新的兄弟节点进入情况四
             }
            //情况四:它的兄弟节点W是黑色,w的右子节点是红色
             swapNodeColor(subNode,nodeP);//将兄弟节点的颜色和其父节点p节点的颜色互换
             setBlack(subNode.right);//右子节点染成黑色
             leftRotate(nodeP);// 对p节点进行左旋
        }
    }

//修复删除节点是其父节点的右子节点情况(和左子节点成镜像)
private void removeRightFlxUp(TNode node){
        TNode nodeP=node.parent;
        TNode subNode=nodeP.left;//删除节点是右节点兄弟节点必然是左节点
        if(null!=subNode&&isRed(subNode)){//兄弟节点为红色
             swapNodeColor(nodeP,subNode);//兄弟节点与其父节点交换颜色
             rightRotate(nodeP);//对父节点右旋
             subNode=node.parent.left;//重新拿到兄弟节点进入情况二
        }
        if((null==subNode.left||isBlack(subNode.left))&&(null==subNode.right||isBlack(subNode.right))){
              if(isRed(nodeP)){
                  swapNodeColor(nodeP,subNode);//兄弟节点与其父节点交换颜色
                  return;
              }
                setRed(subNode);
                removeFlxUp(nodeP);
                return;
        }else{
             if(null==subNode.left||isBlack(subNode.left)){
                 swapNodeColor(subNode,subNode.right);
                 leftRotate(subNode);// 对兄弟节点进行左旋
                 subNode=node.parent.left;//重新拿到兄弟节点进入情况四
             }
             swapNodeColor(subNode,nodeP);//兄弟节点与其父节点交换颜色
             setBlack(subNode.left);//左子节点染成黑色
             rightRotate(nodeP);//对父节点右旋
        }
    }


//测试类
public class MyRbTreeTest {

    public static void main(String[] args) {
      MyRBTree<Integer,Integer> myRBTree=new MyRBTree<>();
        int[] treeNodes=new int[]{1,1,23,0,56,98,12,45,25,66,128,68,15,100};
        for (int treeNode : treeNodes) {
            myRBTree.insert(treeNode, treeNode);
        }
        myRBTree.inOrderPrint();

        TreeOperation<MyRBTree.TNode<Integer,Integer>> treeOperation = new TreeOperation<>();
        treeOperation.show(myRBTree.getRoot());
        System.out.println();
        System.out.println(myRBTree.removeNode(1));
       
        System.out.println(myRBTree.removeNode(100));
       
        System.out.println(myRBTree.removeNode(128));
        

        System.out.println("删除了"+myRBTree.removeNode(23)+"节点");
        System.out.println("删除了"+myRBTree.removeNode(68)+"节点");
         System.out.println(myRBTree.removeNode(98));
          System.out.println(myRBTree.removeNode(15));
          System.out.println(myRBTree.removeNode(66));
          System.out.println(myRBTree.removeNode(12));
          System.out.println(myRBTree.removeNode(0));
          System.out.println(myRBTree.removeNode(25));
         treeOperation.show(myRBTree.getRoot());
    }
}



//树化
package DataStructure.Tree;


import java.lang.reflect.*;
import java.util.HashMap;

/**
 * @author Jerssy
 * @version V1.0
 * @Description
 * @create 2020-11-15 22:12
 */
public class TreeOperation<T> {


    private final HashMap<String,Method> map= new HashMap<>();

     /*
    树的结构示例:
              1
            /   \
          2       3
         / \     / \
        4   5   6   7
    */


    // 用于获得树的层数
    public   int getTreeDepth(T root) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        if (root == null){
            return 0;
        }


        return  (1 + Math.max(getTreeDepth(getTreeFields(root,"left")), getTreeDepth(getTreeFields(root,"right"))));
    }


    private void writeArray(T currNode, int rowIndex, int columnIndex, String[][] res, int treeDepth,int arrayWidth) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {


        // 保证输入的树不为空
        if (currNode == null) return;
        String resNodeValue;

        T key = getTreeFields(currNode, "key");
        T value = getTreeFields(currNode, "value");
        T color=getTreeFields(currNode, "color");


        if (value!=null) {

            resNodeValue=color!=null? ((Boolean) color ?"R-"+value:"B-"+value):value+"";
        }
        else if (key!=null) {

            resNodeValue=color!=null? ((Boolean) color ?"R-"+key:"B-"+key):key+"";
        }
        else {
            System.out.println("key/value"+"不能都为空!");
            return;
        }
        res[rowIndex][columnIndex] = resNodeValue;

        // 计算当前位于树的第几层
        int currLevel = ((rowIndex + 1) / 2);
        // 若到了最后一层,则返回
        if (currLevel == treeDepth) return;
        // 计算当前行到下一行,每个元素之间的间隔(下一行的列索引与当前元素的列索引之间的间隔)
        int gap = treeDepth - currLevel - 1;

        // 对左儿子进行判断,若有左儿子,则记录相应的"/"与左儿子的值
        T left = getTreeFields(currNode, "left");
        if (left != null) {
            int columnI = Math.max(columnIndex - gap - resNodeValue.length()/2,0);
            res[rowIndex + 1][columnI] = "/";
            writeArray(left, rowIndex + 2, Math.max(columnI-gap,0), res, treeDepth,arrayWidth);
         }

        // 对右儿子进行判断,若有右儿子,则记录相应的"\"与右儿子的值
        T right = getTreeFields(currNode, "right");
         if (right != null) {
             int columnI = Math.min(columnIndex + gap +resNodeValue.length()/2,arrayWidth-1);
             res[rowIndex + 1][columnI] = "\\";
             writeArray(right, rowIndex + 2, Math.min(columnI+gap,arrayWidth-1) , res, treeDepth,arrayWidth);
         }
    }


    public void show(T root)   {

        if (root == null){
            System.out.println("EMPTY!");
            return;
        }
        System.out.println();
        // 得到树的深度
        int treeDepth = 0;
        try {
          //通过反射获取泛型类的属性和方法
            Class<?> treeClass = root.getClass();

            Field[] fields = treeClass.getDeclaredFields();
            Method[] declaredMethods = treeClass.getDeclaredMethods();

            for (Field field : fields) {
                field.setAccessible(true);
                String name = field.getName();
                char[] cs=name.toCharArray();
                cs[0]-=32;

                for (Method method : declaredMethods) {
                    method.setAccessible(true);
                    if (method.getName().equals("get" + String.valueOf(cs))) {
                        Method declaredMethod = treeClass.getDeclaredMethod("get" + String.valueOf(cs));
                        declaredMethod.setAccessible(true);
                        map.put(name, declaredMethod);
                    }
                }

            }
            treeDepth = getTreeDepth(root);

        } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }


        // 最后一行的宽度为2的(n - 1)次方乘3,再加1
        // 作为整个二维数组的宽度
        int arrayHeight = treeDepth * 2 - 1;
        int arrayWidth = (2 << (treeDepth - 2)) * 3 + 1;

        int grepWidth=arrayWidth/treeDepth;
        // 用一个字符串数组来存储每个位置应显示的元素
        String[][] res = new String[arrayHeight][arrayWidth+grepWidth];
        // 对数组进行初始化,默认为一个空格

        for (int i = 0; i < arrayHeight; i++) {
            for (int j = 0; j < arrayWidth; j++) {
                res[i][j] = " ";
            }
        }

        // 从根节点开始,递归处理整个树
        // res[0][(arrayWidth + 1)/ 2] = (char)(root.val + '0');
        try {
            writeArray(root, 0, arrayWidth / 2, res, treeDepth,arrayWidth+grepWidth);
        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }

        // 此时,已经将所有需要显示的元素储存到了二维数组中,将其拼接并打印即可
        for (String[] line : res) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < line.length; i++) {
                if (line[i]!=null){
                    sb.append(line[i]);
                    if (line[i].length() > 1 && i <= line.length - 1) {
                        i += line[i].length() > 4 ? line[i].length()-2 : line[i].length() - 1;
                    }
                }

            }
            System.out.println(sb);
        }
    }

    private T getTreeFields(T node,String name) throws InvocationTargetException, IllegalAccessException {

          if (map.containsKey(name)){

              return (T) map.get(name).invoke(node);
          }

          return null;
    }

}

插入示例:
在这里插入图片描述
删除示例
在这里插入图片描述
完整代码 java 红黑树实现

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值