• 《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 等地方被使用。

  • 相关阅读:
    虚函数和纯虚函数
    MS CRM 2011中PartyList类型字段的实例化
    MS CRM 2011的自定义与开发(12)——表单脚本扩展开发(4)
    MS CRM 2011的自定义与开发(12)——表单脚本扩展开发(2)
    MS CRM 2011的自定义和开发(10)——CRM web服务介绍(第二部分)——IOrganizationService(二)
    MS CRM 2011 SDK 5.08已经发布
    MS CRM 2011 Q2的一些更新
    最近很忙
    Microsoft Dynamics CRM 2011最近的一些更新
    补一篇,Update Rollup 12 终于发布了
  • 原文地址:https://www.cnblogs.com/jssj/p/13873485.html
Copyright © 2020-2023  润新知