栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java之红黑树

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java之红黑树

目录

1、简介

2、红黑树的特点

3、插入和删除造成不平衡的修复方法

3.1 变色

 3.2 右旋

 3.3 左旋

4、完整实现红黑树


1、简介

红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。

红黑树是一种特化的AVL树(平衡二叉树),都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。

它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。

红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但 对之进行平衡的代价较低, 其平均统计性能要强于 AVL 。

由于每一棵红黑树都是一颗二叉排序树,因此,在对红黑树进行查找时,可以采用运用于普通二叉排序树上的查找算法,在查找过程中不需要颜色信息。

2、红黑树的特点

红黑树是每个结点都带有颜色属性的二叉查找树,颜色或红色或黑色。 在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:

  1. 性质一:每个节点要么是黑色,要么是红色
  2. 性质二:根节点是黑色
  3. 性质三:每个叶子节点(NIL即NULL)
  4. 性质四:每个红色节点的两个子节点一定是黑色
  5. 性质五:任意一节点到两个子节点的路径都包含相同的黑节点,俗称黑高

如下图:

3、插入和删除造成不平衡的修复方法

当我们在对红黑树进行插入和删除等操作时,对树做了修改,那么可能会违背红黑树的性质。

为了保持红黑树的性质,我们可以对相关结点做一系列的调整,通过对树进行旋转(例如左旋和右旋操作),即修改树中某些结点的颜色及指针结构,以达到对红黑树进行插入、删除结点等操作时,红黑树依然能保持它特有的性质(五点性质)。

3.1 变色

所谓变色当然是红黑互换颜色,当然根节点还是黑色。

                            

 3.2 右旋

下图我们可以发下在右子树有两个红色节点相连,这不符合上面提过的性质5,我们需要对它变色右旋。

第一步:先让L和LL变成相反的颜色,第二步以L的位置为旋转点让root的左孩子变成LL,L变成RL的右孩子,如果LLR有右孩子,LL的右孩子变成L的左孩子。此时,已恢复红黑树的平衡了。

 

 右旋代码如下:

 
    private void rightRotate(RBNode y){
        RBNode x=y.left;
        //1.将y的左子节点指向x的右子节点,并更新x的右子节点的父亲节点为y
        y.left=x.right;
        if(x.right!=null){
            x.right.parent=y;
        }

        //2.当y的父节点不为空时,更新x的父节点为y的父节点,更新y的父节点指定子节点为x
        if(y.parent!=null){
            x.parent=y.parent;
            if(y == y.parent.left){//判断是否为父节点左孩子
                y.parent.left=x;
            }else{
                y.parent.right=x;
            }
        }else{//y为根节点
            this.root=x;
            this.root.parent=null;
        }

        //3.更新y的父节点为x,更新x的右子节点为y
        y.parent=x;
        x.right=y;
    }

 3.3 左旋

可以看到下图RR双红,首先我们还是先将LR和其父节点L变色,然后左旋恢复红黑树平衡

第一步变色

 

 第二步左旋 让root的左孩子变为LR 同时L变成LR的左孩子,如果LR有左孩子,需变为L右孩子。

 代码实现如下:

 
      private void leftRotate(RBNode x){
          RBNode y=x.right;
          //1.将x的右子结点指向y的左子结点(ly),将y的左子节点父节点更新为x
          x.right=y.left;
          if(y.left!=null){
              y.left.parent=x;
          }
          //2.当x的父节点不为null时,更新y的父节点为x的父节点指定子树(当时x子树的位置) 指定为y
          if(x.parent != null){
              y.parent=x.parent;

              if(x==x.parent.left){
                  x.parent.left=y;
              }else{
                  x.parent.right=y;
              }
          }else{
              this.root=y;
              this.root.parent=null;
          }
          //3.将x的父节点更新为y,将y的左子结点更新为x
          x.parent=y;
          y.left=x;
      }

4、完整实现红黑树
public class NewRBTree,V>{
    private final boolean RED=true;

    private final boolean BLACK=false;

    private Node root;


    class Node,V>{

        private K key;

        private V value;

        private boolean color;

        private Node parent;

        private Node left;

        private Node right;

        public Node(K key, V value) {
            this.key = key;
            this.value = value;
        }

        public boolean isColor() {
            return color;
        }

        public void setColor(boolean color) {
            this.color = color;
        }

        public Node getLeft() {
            return this.left;
        }

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }

        public Node getParent() {
            return parent;
        }

        public Node getRight() {
            return right;
        }
    }
    
    private void leftRotate(Node x){

        Node y=x.right;

        x.right=y.left;

        if(x.parent!=null){
            y.parent=x.parent;

            if(x==x.parent.left){
                x.parent.left=y;
            }else{
                x.parent.right=y;
            }
        }else{
            this.root=y;
            y.parent=null;
        }

        x.parent=y;

        y.left=x;
    }
    
    private void RightRotate(Node y){
       // 1.将x的右子节点指向y的左子节点,并更新y的左子节点的父亲节点为x
        Node x=y.left;

        y.left=x.right;
        // 2.当y的父节点不为空时,更新y的父节点为x的父节点
        if(y.parent!=null){
            x.parent=y.parent;
            //判断y是其父节点的左孩子还有右孩子
            if(y==y.parent.left){
                y.parent.left=x;
            }else{
                y.parent.right=x;
            }

        }else{
            this.root=x;
            x.parent=null;
        }
        //3.更新y的父节点为x,更新x的右子节点为y
        y.parent=x;
        x.right=y;

    }

    //是否是红色
    private boolean isRed(Node node){
        return node.color==RED;
    }
    //是否是红色
    private boolean isBlack(Node node){
        return node.color==BLACK;
    }
    //返回节点的父亲
    private Node parentOf(Node node){
        return node.parent;
    }
    //设置红色
    private void setRED(Node node){
        node.color=RED;
    }
    //设置黑色
    public void setBLACK(Node node){
        node.color=BLACK;
    }
    //添加
    public void insert(K key,V value){
        Node node=new Node<>(key,value);
        node.setColor(RED);
        insert(node);
    }

    private void insert(Node node){
        Node newRoot=root;
        Node parent=null;

        while(newRoot!=null){
            parent=newRoot;
            if(node.key.compareTo(newRoot.key)>0){
                newRoot=newRoot.right;
            }else if(node.key.compareTo(newRoot.key)<0){
                newRoot=newRoot.left;
            }else{
                newRoot.value=node.value;
                return;
            }
        }
        node.parent=parent;

        if(parent==null){
            this.root=node;
        }else if(parent.key.compareTo(node.key)>0){
            parent.left=node;
        }else{
            parent.right=node;
        }

        insertFixUp(node);//修复红黑树
    }

    
    private void insertFixUp(Node node){
        Node parent=node.parent;

        this.root.setColor(BLACK);
        //情景4:插入节点的父亲节点为红色
         if(parent!=null && isRed(parent)){
             //情景4.1:叔叔节点存在,并且为红色(父-叔双红),将爸爸和叔叔染成黑色,爷爷染成红色,进行下一轮处理
             Node gParent=parent.parent;
             Node uncle=null;
             int cmp=gParent.key.compareTo(parent.key);
             if(cmp<0){
                 uncle=gParent.left;
             }else if(cmp>0){
                 uncle=gParent.right;
             }

             if(uncle!=null && isRed(uncle)){
                 setBLACK(uncle);
                 setBLACK(parent);
                 setRED(gParent);
                 insertFixUp(node);
                 return;
             }

            if((uncle==null || isBlack(uncle)) && cmp>0){
                if(parent.key.compareTo(node.key)>0){
                     setBLACK(parent);
                     setRED(gParent);
                     RightRotate(gParent);
                     return;
                }else{
                     leftRotate(parent);
                     insertFixUp(parent);
                     return;
                }
            }else{
                if(parent.key.compareTo(node.key)>0){
                    RightRotate(parent);
                    insertFixUp(parent);
                    return;
                }else{
                    setBLACK(parent);
                    setRED(gParent);
                    leftRotate(gParent);
                    return;
                }
            }

         }
    }

    //中序打印
    public void inOrderPrint(){

        if(this.root==null) return;

        inOrderPrint(this.root);
    }

    public Node getRoot(){
        return this.root;
    }

    private void inOrderPrint(Node node){
        if(node==null) return;

        inOrderPrint(node.left);
        System.out.println("key:"+node.key+",value:"+node.value);
        inOrderPrint(node.right);
    }

}

 最后再附上验证打印红黑树的类TreeOperation

public class TreeOperation {
      

    // 用于获得树的层数
    public static int getTreeDepth(NewRBTree.Node root) {
        return root == null ? 0 : (1 + Math.max(getTreeDepth(root.getLeft()), getTreeDepth(root.getRight())));
    }


    private static void writeArray(NewRBTree.Node currNode, int rowIndex, int columnIndex, String[][] res, int treeDepth) {
        // 保证输入的树不为空
        if (currNode == null) return;
        // 先将当前节点保存到二维数组中
        res[rowIndex][columnIndex] = String.valueOf(currNode.getKey() + "-" + (currNode.isColor() ? "R" : "B") + "");

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

        // 对左儿子进行判断,若有左儿子,则记录相应的"/"与左儿子的值
        if (currNode.getLeft() != null) {
            res[rowIndex + 1][columnIndex - gap] = "/";
            writeArray(currNode.getLeft(), rowIndex + 2, columnIndex - gap * 2, res, treeDepth);
        }

        // 对右儿子进行判断,若有右儿子,则记录相应的""与右儿子的值
        if (currNode.getRight() != null) {
            res[rowIndex + 1][columnIndex + gap] = "\";
            writeArray(currNode.getRight(), rowIndex + 2, columnIndex + gap * 2, res, treeDepth);
        }
    }


    public static void show(NewRBTree.Node root) {
        if (root == null) System.out.println("EMPTY!");
        // 得到树的深度
        int treeDepth = getTreeDepth(root);

        // 最后一行的宽度为2的(n - 1)次方乘3,再加1
        // 作为整个二维数组的宽度
        int arrayHeight = treeDepth * 2 - 1;
        int arrayWidth = (2 << (treeDepth - 2)) * 3 + 1;
        // 用一个字符串数组来存储每个位置应显示的元素
        String[][] res = new String[arrayHeight][arrayWidth];
        // 对数组进行初始化,默认为一个空格
        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');
        writeArray(root, 0, arrayWidth/ 2, res, treeDepth);

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/845360.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号