zoukankan      html  css  js  c++  java
  • 《Java数据结构》红黑树(RBTree)

    前言

    在学习JDK8的HashMap的时候遇到了红黑树,由于之前没有遇到过红黑树,所以借这个机会好好了解一下红黑树。

    原理

    先看一下红黑树的定义:

    1.每个结点或是红的,或是黑的
    2.根节点是黑的
    3.每个叶结点是黑的
    4.如果一个结点是红的,则它的两个儿子都是黑的
    5.对每个结点,从该结点到其任何叶子节点的所有路径上包含相同数目的黑结点

    上图就是一颗红黑树,那为什么需要红黑树?

    这个需要说另外一种数据结果:平衡二叉树

    查询效率对比:平衡二叉树略好。

    插入,删除效率对比:红黑树效率高。这也是红黑树产生的原因。(感叹创造红黑树数据结构的人,真的牛!)

    代码

    public class RBTree<T extends Comparable<T>> {
    
        private RBTNode<T> mRoot;    // 根结点
    
        private static final boolean RED   = false;
        private static final boolean BLACK = true;
    
        public class RBTNode<T extends Comparable<T>> {
            boolean color;        // 颜色
            T key;                // 关键字(键值)
            RBTNode<T> left;    // 左孩子
            RBTNode<T> right;    // 右孩子
            RBTNode<T> parent;    // 父结点
    
            public RBTNode(T key, boolean color, RBTNode<T> parent, RBTNode<T> left, RBTNode<T> right) {
                this.key = key;
                this.color = color;
                this.parent = parent;
                this.left = left;
                this.right = right;
            }
    
            public T getKey() {
                return key;
            }
    
            public String toString() {
                return ""+key+(this.color==RED?"(R)":"B");
            }
        }
    
        public RBTree() {
            mRoot=null;
        }
    
        private RBTNode<T> parentOf(RBTNode<T> node) {
            return node!=null ? node.parent : null;
        }
        private boolean colorOf(RBTNode<T> node) {
            return node!=null ? node.color : BLACK;
        }
        private boolean isRed(RBTNode<T> node) {
            return ((node!=null)&&(node.color==RED)) ? true : false;
        }
        private boolean isBlack(RBTNode<T> node) {
            return !isRed(node);
        }
        private void setBlack(RBTNode<T> node) {
            if (node!=null)
                node.color = BLACK;
        }
        private void setRed(RBTNode<T> node) {
            if (node!=null)
                node.color = RED;
        }
        private void setParent(RBTNode<T> node, RBTNode<T> parent) {
            if (node!=null)
                node.parent = parent;
        }
        private void setColor(RBTNode<T> node, boolean color) {
            if (node!=null)
                node.color = color;
        }
    
        /**
         * (递归实现)查找"红黑树x"中键值为key的节点
         */
        private RBTNode<T> search(RBTNode<T> x, T key) {
            if (x==null)
                return x;
    
            int cmp = key.compareTo(x.key);
            if (cmp < 0)
                return search(x.left, key);
            else if (cmp > 0)
                return search(x.right, key);
            else
                return x;
        }
    
        public RBTNode<T> search(T key) {
            return search(mRoot, key);
        }
    
        /**
         * (非递归实现)查找"红黑树x"中键值为key的节点
         */
        private RBTNode<T> iterativeSearch(RBTNode<T> x, T key) {
            while (x!=null) {
                int cmp = key.compareTo(x.key);
    
                if (cmp < 0)
                    x = x.left;
                else if (cmp > 0)
                    x = x.right;
                else
                    return x;
            }
    
            return x;
        }
    
        public RBTNode<T> iterativeSearch(T key) {
            return iterativeSearch(mRoot, key);
        }
    
        /**
         * 查找最小结点:返回tree为根结点的红黑树的最小结点。
         */
        private RBTNode<T> minimum(RBTNode<T> tree) {
            if (tree == null)
                return null;
    
            while(tree.left != null)
                tree = tree.left;
            return tree;
        }
    
        public T minimum() {
            RBTNode<T> p = minimum(mRoot);
            if (p != null)
                return p.key;
    
            return null;
        }
    
        /**
         * 查找最大结点:返回tree为根结点的红黑树的最大结点。
         */
        private RBTNode<T> maximum(RBTNode<T> tree) {
            if (tree == null)
                return null;
    
            while(tree.right != null)
                tree = tree.right;
            return tree;
        }
    
        public T maximum() {
            RBTNode<T> p = maximum(mRoot);
            if (p != null)
                return p.key;
    
            return null;
        }
    
        /**
         * 找结点(x)的后继结点。即,查找"红黑树中数据值大于该结点"的"最小结点"。
         */
        public RBTNode<T> successor(RBTNode<T> x) {
            // 如果x存在右孩子,则"x的后继结点"为 "以其右孩子为根的子树的最小结点"。
            if (x.right != null)
                return minimum(x.right);
    
            // 如果x没有右孩子。则x有以下两种可能:
            // (01) x是"一个左孩子",则"x的后继结点"为 "它的父结点"。
            // (02) x是"一个右孩子",则查找"x的最低的父结点,并且该父结点要具有左孩子",找到的这个"最低的父结点"就是"x的后继结点"。
            RBTNode<T> y = x.parent;
            while ((y!=null) && (x==y.right)) {
                x = y;
                y = y.parent;
            }
    
            return y;
        }
    
        /**
         * 找结点(x)的前驱结点。即,查找"红黑树中数据值小于该结点"的"最大结点"。
         */
        public RBTNode<T> predecessor(RBTNode<T> x) {
            // 如果x存在左孩子,则"x的前驱结点"为 "以其左孩子为根的子树的最大结点"。
            if (x.left != null)
                return maximum(x.left);
    
            // 如果x没有左孩子。则x有以下两种可能:
            // (01) x是"一个右孩子",则"x的前驱结点"为 "它的父结点"。
            // (01) x是"一个左孩子",则查找"x的最低的父结点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"x的前驱结点"。
            RBTNode<T> y = x.parent;
            while ((y!=null) && (x==y.left)) {
                x = y;
                y = y.parent;
            }
    
            return y;
        }
    
        /**
         * 对红黑树的节点(x)进行左旋转
         * 左旋示意图(对节点x进行左旋):
         *      px                              px
         *     /                               /
         *    x                               y
         *   /        --(左旋)-.           /                 #
         *  lx   y                          x  ry
         *     /                          /  
         *    ly   ry                     lx  ly
         *
         *
         */
        private void leftRotate(RBTNode<T> x) {
            // 设置x的右孩子为y
            RBTNode<T> y = x.right;
    
            // 将 “y的左孩子” 设为 “x的右孩子”;
            // 如果y的左孩子非空,将 “x” 设为 “y的左孩子的父亲”
            x.right = y.left;
            if (y.left != null)
                y.left.parent = x;
    
            // 将 “x的父亲” 设为 “y的父亲”
            y.parent = x.parent;
    
            if (x.parent == null) {
                this.mRoot = y;            // 如果 “x的父亲” 是空节点,则将y设为根节点
            } else {
                if (x.parent.left == x)
                    x.parent.left = y;    // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
                else
                    x.parent.right = y;    // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
            }
    
            // 将 “x” 设为 “y的左孩子”
            y.left = x;
            // 将 “x的父节点” 设为 “y”
            x.parent = y;
        }
    
        /**
         * 对红黑树的节点(y)进行右旋转
         * 右旋示意图(对节点y进行左旋):
         *            py                               py
         *           /                                /
         *          y                                x
         *         /        --(右旋)-.            /                       #
         *        x   ry                           lx   y
         *       /                                    /                    #
         *      lx  rx                                rx  ry
         *
         */
        private void rightRotate(RBTNode<T> y) {
            // 设置x是当前节点的左孩子。
            RBTNode<T> x = y.left;
    
            // 将 “x的右孩子” 设为 “y的左孩子”;
            // 如果"x的右孩子"不为空的话,将 “y” 设为 “x的右孩子的父亲”
            y.left = x.right;
            if (x.right != null)
                x.right.parent = y;
    
            // 将 “y的父亲” 设为 “x的父亲”
            x.parent = y.parent;
    
            if (y.parent == null) {
                this.mRoot = x;            // 如果 “y的父亲” 是空节点,则将x设为根节点
            } else {
                if (y == y.parent.right)
                    y.parent.right = x;    // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子”
                else
                    y.parent.left = x;    // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子”
            }
    
            // 将 “y” 设为 “x的右孩子”
            x.right = y;
    
            // 将 “y的父节点” 设为 “x”
            y.parent = x;
        }
    
        /**
         * 红黑树插入修正函数
         * 在向红黑树中插入节点之后(失去平衡),再调用该函数;
         * 目的是将它重新塑造成一颗红黑树。
         * 参数说明:
         *     node 插入的结点        // 对应《算法导论》中的z
         */
        private void insertFixUp(RBTNode<T> node) {
            RBTNode<T> parent, gparent;
    
            // 若“父节点存在,并且父节点的颜色是红色”
            while (((parent = parentOf(node))!=null) && isRed(parent)) {
                gparent = parentOf(parent);
    
                //若“父节点”是“祖父节点的左孩子”
                if (parent == gparent.left) {
                    // Case 1条件:叔叔节点是红色
                    RBTNode<T> uncle = gparent.right;
                    if ((uncle!=null) && isRed(uncle)) {
                        setBlack(uncle);
                        setBlack(parent);
                        setRed(gparent);
                        node = gparent;
                        continue;
                    }
    
                    // Case 2条件:叔叔是黑色,且当前节点是右孩子
                    if (parent.right == node) {
                        RBTNode<T> tmp;
                        leftRotate(parent);
                        tmp = parent;
                        parent = node;
                        node = tmp;
                    }
    
                    // Case 3条件:叔叔是黑色,且当前节点是左孩子。
                    setBlack(parent);
                    setRed(gparent);
                    rightRotate(gparent);
                } else {    //若“z的父节点”是“z的祖父节点的右孩子”
                    // Case 1条件:叔叔节点是红色
                    RBTNode<T> uncle = gparent.left;
                    if ((uncle!=null) && isRed(uncle)) {
                        setBlack(uncle);
                        setBlack(parent);
                        setRed(gparent);
                        node = gparent;
                        continue;
                    }
    
                    // Case 2条件:叔叔是黑色,且当前节点是左孩子
                    if (parent.left == node) {
                        RBTNode<T> tmp;
                        rightRotate(parent);
                        tmp = parent;
                        parent = node;
                        node = tmp;
                    }
    
                    // Case 3条件:叔叔是黑色,且当前节点是右孩子。
                    setBlack(parent);
                    setRed(gparent);
                    leftRotate(gparent);
                }
            }
    
            // 将根节点设为黑色
            setBlack(this.mRoot);
        }
    
        /*
         * 将结点插入到红黑树中
         *
         * 参数说明:
         *     node 插入的结点        // 对应《算法导论》中的node
         */
        private void insert(RBTNode<T> node) {
            int cmp;
            RBTNode<T> y = null;
            RBTNode<T> x = this.mRoot;
    
            // 1. 将红黑树当作一颗二叉查找树,将节点添加到二叉查找树中。
            while (x != null) {
                y = x;
                cmp = node.key.compareTo(x.key);
                if (cmp < 0)
                    x = x.left;
                else
                    x = x.right;
            }
    
            node.parent = y;
            if (y!=null) {
                cmp = node.key.compareTo(y.key);
                if (cmp < 0)
                    y.left = node;
                else
                    y.right = node;
            } else {
                this.mRoot = node;
            }
    
            // 2. 设置节点的颜色为红色
            node.color = RED;
    
            // 3. 将它重新修正为一颗二叉查找树
            insertFixUp(node);
        }
    
        /**
         * 新建结点(key),并将其插入到红黑树中
         * 参数说明:
         *     key 插入结点的键值
         */
        public void insert(T key) {
            RBTNode<T> node=new RBTNode<T>(key,BLACK,null,null,null);
            // 如果新建结点失败,则返回。
            if (node != null)
                insert(node);
        }
    
    
        /**
         * 红黑树删除修正函数
         * 在从红黑树中删除插入节点之后(红黑树失去平衡),再调用该函数;
         * 目的是将它重新塑造成一颗红黑树。
         * 参数说明:
         *     node 待修正的节点
         */
        private void removeFixUp(RBTNode<T> node, RBTNode<T> parent) {
            RBTNode<T> other;
    
            while ((node==null || isBlack(node)) && (node != this.mRoot)) {
                if (parent.left == node) {
                    other = parent.right;
                    if (isRed(other)) {
                        // Case 1: x的兄弟w是红色的
                        setBlack(other);
                        setRed(parent);
                        leftRotate(parent);
                        other = parent.right;
                    }
                    if ((other.left==null || isBlack(other.left)) &&
                            (other.right==null || isBlack(other.right))) {
                        // Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的
                        setRed(other);
                        node = parent;
                        parent = parentOf(node);
                    } else {
                        if (other.right==null || isBlack(other.right)) {
                            // Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。
                            setBlack(other.left);
                            setRed(other);
                            rightRotate(other);
                            other = parent.right;
                        }
                        // Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
                        setColor(other, colorOf(parent));
                        setBlack(parent);
                        setBlack(other.right);
                        leftRotate(parent);
                        node = this.mRoot;
                        break;
                    }
                } else {
                    other = parent.left;
                    if (isRed(other)) {
                        // Case 1: x的兄弟w是红色的
                        setBlack(other);
                        setRed(parent);
                        rightRotate(parent);
                        other = parent.left;
                    }
    
                    if ((other.left==null || isBlack(other.left)) &&
                            (other.right==null || isBlack(other.right))) {
                        // Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的
                        setRed(other);
                        node = parent;
                        parent = parentOf(node);
                    } else {
    
                        if (other.left==null || isBlack(other.left)) {
                            // Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。
                            setBlack(other.right);
                            setRed(other);
                            leftRotate(other);
                            other = parent.left;
                        }
    
                        // Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
                        setColor(other, colorOf(parent));
                        setBlack(parent);
                        setBlack(other.left);
                        rightRotate(parent);
                        node = this.mRoot;
                        break;
                    }
                }
            }
    
            if (node!=null)
                setBlack(node);
        }
    
        /**
         * 删除结点(node),并返回被删除的结点
         * 参数说明:
         *     node 删除的结点
         */
        private void remove(RBTNode<T> node) {
            RBTNode<T> child, parent;
            boolean color;
    
            // 被删除节点的"左右孩子都不为空"的情况。
            if ( (node.left!=null) && (node.right!=null) ) {
                // 被删节点的后继节点。(称为"取代节点")
                // 用它来取代"被删节点"的位置,然后再将"被删节点"去掉。
                RBTNode<T> replace = node;
    
                // 获取后继节点
                replace = replace.right;
                while (replace.left != null)
                    replace = replace.left;
    
                // "node节点"不是根节点(只有根节点不存在父节点)
                if (parentOf(node)!=null) {
                    if (parentOf(node).left == node)
                        parentOf(node).left = replace;
                    else
                        parentOf(node).right = replace;
                } else {
                    // "node节点"是根节点,更新根节点。
                    this.mRoot = replace;
                }
    
                // child是"取代节点"的右孩子,也是需要"调整的节点"。
                // "取代节点"肯定不存在左孩子!因为它是一个后继节点。
                child = replace.right;
                parent = parentOf(replace);
                // 保存"取代节点"的颜色
                color = colorOf(replace);
    
                // "被删除节点"是"它的后继节点的父节点"
                if (parent == node) {
                    parent = replace;
                } else {
                    // child不为空
                    if (child!=null)
                        setParent(child, parent);
                    parent.left = child;
    
                    replace.right = node.right;
                    setParent(node.right, replace);
                }
    
                replace.parent = node.parent;
                replace.color = node.color;
                replace.left = node.left;
                node.left.parent = replace;
    
                if (color == BLACK)
                    removeFixUp(child, parent);
    
                node = null;
                return ;
            }
    
            if (node.left !=null) {
                child = node.left;
            } else {
                child = node.right;
            }
    
            parent = node.parent;
            // 保存"取代节点"的颜色
            color = node.color;
    
            if (child!=null)
                child.parent = parent;
    
            // "node节点"不是根节点
            if (parent!=null) {
                if (parent.left == node)
                    parent.left = child;
                else
                    parent.right = child;
            } else {
                this.mRoot = child;
            }
    
            if (color == BLACK)
                removeFixUp(child, parent);
            node = null;
        }
    
        /**
         * 删除结点(z),并返回被删除的结点
         * 参数说明:
         *     tree 红黑树的根结点
         *     z 删除的结点
         */
        public void remove(T key) {
            RBTNode<T> node;
            if ((node = search(mRoot, key)) != null)
                remove(node);
        }
    
        /**
         * 销毁红黑树
         */
        private void destroy(RBTNode<T> tree) {
            if (tree==null)
                return ;
            if (tree.left != null)
                destroy(tree.left);
            if (tree.right != null)
                destroy(tree.right);
            tree=null;
        }
    
        public void clear() {
            destroy(mRoot);
            mRoot = null;
        }
    
        /**
         * 打印"红黑树"
         * key        -- 节点的键值
         * direction  --  0,表示该节点是根节点;
         *               -1,表示该节点是它的父结点的左孩子;
         *                1,表示该节点是它的父结点的右孩子。
         */
        private void print(RBTNode<T> tree, T key, int direction) {
            if(tree != null) {
                if(direction==0)    // tree是根节点
                    System.out.printf("%2d(B) is root
    ", tree.key);
                else                // tree是分支节点
                    System.out.printf("%2d(%s) is %2d's %6s child
    ", tree.key, isRed(tree)?"R":"B", key, direction==1?"right" : "left");
    
                print(tree.left, tree.key, -1);
                print(tree.right,tree.key,  1);
            }
        }
    
        public void print() {
            if (mRoot != null)
                print(mRoot, mRoot.key, 0);
        }
    }
    public class RBTreeTest {
    
        private static final int a[] = {10, 40, 30, 60, 90, 70, 20, 50, 80};
        private static final boolean mDebugInsert = false;    // "插入"动作的检测开关(false,关闭;true,打开)
        private static final boolean mDebugDelete = false;    // "删除"动作的检测开关(false,关闭;true,打开)
    
        public static void main(String[] args) {
            int i, iLen = a.length;
            RBTree<Integer> tree=new RBTree<Integer>();
    
            System.out.printf("插入红黑树节点: ");
            for(i=0; i<iLen; i++){
                System.out.printf("%d ", a[i]);
                tree.insert(a[i]);
                // 设置mDebugInsert=true,测试"添加函数"
                if (mDebugInsert) {
                    System.out.printf("== 添加节点: %d
    ", a[i]);
                    System.out.printf("== 树的详细信息: 
    ");
                    tree.print();
                    System.out.printf("
    ");
                }
            }
    
            System.out.printf("
    ");
            System.out.printf("树的详细信息: 
    ");
            tree.print();
            System.out.printf("
    ");
    
            // 设置mDebugDelete=true,测试"删除函数"
            if (mDebugDelete) {
                for(i=0; i<iLen; i++){
                    tree.remove(a[i]);
                    System.out.printf("== 删除节点: %d
    ", a[i]);
                    System.out.printf("== 树的详细信息: 
    ");
                    tree.print();
                    System.out.printf("
    ");
                }
            }
    
            // 销毁二叉树
            tree.clear();
        }
    }

    运行结果:

    总结

    红黑树的重要作用就是为了查询速度保持的情况下插入提速。在linux, Java HashMap 等地方被使用。

  • 相关阅读:
    ASP.NET MVC HandleError异常过滤器过滤器用法
    ASP.NET MVC 4 过滤器(Authorize)
    ASP.NET MVC 4 过滤器(Authorize)
    蜜蜂路线
    1220 数字三角形
    Ⅳ.Catalan数
    10:单词排序
    09:明明的随机数
    07:合影效果
    08:病人排队
  • 原文地址:https://www.cnblogs.com/jssj/p/13873485.html
Copyright © 2011-2022 走看看