美文网首页
手写红黑树,实现增、删、查功能

手写红黑树,实现增、删、查功能

作者: jqboooo | 来源:发表于2018-12-22 22:17 被阅读0次

    红黑树

    1、概念

    红黑树(RBT)的定义:它或者是一颗空树,或者是具有一下性质的二叉查找树。

    1. 节点非红即黑
    2. 根节点是黑色
    3. 所有NULL节点称为叶子节点,且认为颜色为黑色
    4. 所有红节点的子节点都为黑色
    5. 从任一节点到其叶子节点的所有路径上都包含相同数目的黑节点

    对平衡树的改进:任意一个节点,它的左右子树的层次最多不超过一倍。

    1.png

    2、插入节点

    1. 插入节点:先按照二叉排序树的方式插入一个节点(红色)
    2. 插入节点是根节点: 解决:直接将节点涂黑
    3. 插入节点的父节点是黑色: 不违背任何性质,不用调整
    4. 插入节点的父节点是红色,那么分以下几种情况:

    第一种情况:父节点是祖父节点的左孩子

    情况1:祖父节点的另一个子节点(叔叔节点)是红色
    对策: 将当前节点的父节点和叔叔节点涂黑,祖父节点涂红,把当前节点指向祖父节点,从新的当前节点开始算法
    
    2.png
    情况2:叔叔节点是黑色,右分两种情况:
    情况2.1:当前节点是其父节点的右孩子
    对策:当前节点的父节点做为新的当前节点,以新当前节点为支点左旋。
    
    3.png
    情况2.2:当前节点是其父节点的左孩子
    对策:父节点变为黑色,祖父节点变红色,再祖父节点为支点进行右旋
    
    4.png

    第二种情况:父节点是祖父节点的右孩子(和上面情况一样,将左全部变成右即可)

    3、删除节点

    删除节点:先进行二叉排序树的删除操作,然后已替换节点作为当前节点进行后面的平衡操作

    1. 当前节点是红色。对策:直接把当前节点染成黑色,结束。

    2. 当前节点x是黑色,分以下几种情况:

    第一种情况:被删除节点是父节点的左孩子

    2.1 当前节点是根节点
    对策:什么都不做
    
    2.2 当前节点x的兄弟节点是红色(此时父节点和兄弟节点的子节点分为黑)
    对策:把父节点染成红色,兄弟节点染成黑色,对父节点进行左旋,重新设置x的兄弟节点
    
    5.png
    2.3 当前节点x 的兄弟节点是黑色
    2.3.1 兄弟节点的两个孩子都是黑色
    对策:将x的兄弟节点设为红色,设置x的父节点为新的x节点
    
    6.png
    2.3.2 兄弟的右孩子是黑色,左孩子是红色
    对策:将x兄弟节点的左孩子设为黑色,将x兄弟节点设置红色,将x的兄弟节点右旋,右旋后,重新设置x的兄弟节点。
    
    7.png
    2.3.3 兄弟节点的右孩子是红色
    对策:把兄弟节点染成当前节点父节点颜色,把当前节点父节点染成黑色,兄弟节点右孩子染成黑色,再以当前节点的父节点为支点进行左旋,算法结算
    
    8.png

    第二种情况:被删除节点是父节点的右孩子(对策: 把第一种情况的左设置为右)

    4、完整代码(代码有详细的注释)

    import java.util.LinkedList;
    
    /**
     * 红黑树
     * <p>
     * author: bobo
     * create time: 2018/12/18 3:39 PM
     * email: jqbo84@163.com
     */
    public class RBTree<E extends Comparable<E>> {
    
        private static final boolean BLACK = true;
        private static final boolean RED = false;
    
        private Node<E> root;
    
        private int size;
    
        public Node<E> getRoot() {
            return root;
        }
    
        public int getSize() {
            return size;
        }
    
        /**
         * 获取颜色值
         *
         * @param p
         */
        private boolean colorOf(Node<E> p) {
            return (p == null ? BLACK : p.color);
        }
    
        /**
         * 获取父节点
         *
         * @param p
         * @return
         */
        private Node<E> parentOf(Node<E> p) {
            return (p == null ? null : p.parent);
        }
    
        /**
         * 设置节点的颜色
         *
         * @param p
         * @param color
         */
        private void setColor(Node<E> p, boolean color) {
            if (p != null) {
                p.color = color;
            }
        }
    
        /**
         * 获取当前节点的左节点
         *
         * @param p
         * @return
         */
        private Node<E> leftOf(Node<E> p) {
            return (p == null ? null : p.leftChild);
        }
    
        /**
         * 获取当前节点的右节点
         *
         * @param p
         * @return
         */
        private Node<E> rightOf(Node<E> p) {
            return (p == null ? null : p.rightChild);
        }
    
        /**
         * 左旋转
         *
         * @param x
         */
        public void leftRotate(Node<E> x) {
            if (x == null) {
                return;
            }
            //1.先取到x的右结点
            Node<E> y = x.rightChild;
            //2.把𝞫接上x的右结点上
            x.rightChild = y.leftChild;
            if (y.leftChild != null) {
                y.leftChild.parent = x;
            }
            //3.把y移到x的父结点上
            y.parent = x.parent;
            if (x.parent == null) {
                root = y;
            } else {
                if (x.parent.leftChild == x) {
                    x.parent.leftChild = y;
                } else {
                    x.parent.rightChild = y;
                }
            }
            y.leftChild = x;
            x.parent = y;
        }
    
        /**
         * 右旋转
         *
         * @param x
         */
        public void rightRotate(Node<E> x) {
            if (x == null) {
                return;
            }
            //1.先取到x的左结点
            Node<E> y = x.leftChild;
            //2.把𝞫接上x的左结点上
            x.leftChild = y.rightChild;
            if (y.rightChild != null) {
                y.rightChild.parent = x;
            }
            //3.把y移到x的父结点上
            y.parent = x.parent;
            if (x.parent == null) {
                root = y;
            } else {
                if (x.parent.leftChild == x) {
                    x.parent.leftChild = y;
                } else {
                    x.parent.rightChild = y;
                }
            }
            y.rightChild = x;
            x.parent = y;
        }
    
        /**
         * 插入节点:先按照二叉排序树的方式插入一个节点,再查找最小不平衡子树,以最小不平衡子树进行下面的操作
         *
         * @param element
         * @return
         */
        public boolean insertElement(E element) {
            if (element == null) {
                return false;
            }
            Node<E> t = root;
            if (t == null) {
                root = new Node<>(element, null);
                size = 1;
                return true;
            }
            int cmp = 0;
            Node<E> parent = null;
            Comparable<E> e = element;
            //1.查找可以插入的位置
            do {
                parent = t;
                cmp = e.compareTo(t.element);
                if (cmp < 0) {//比父结点小,那么查左结点
                    t = t.leftChild;
                } else if (cmp > 0) {//比父结点大,那么查右结点
                    t = t.rightChild;
                } else {//相等,说明是同一个数据,不需要插入
                    return false;
                }
            } while (t != null);
            //2.找到可以插入的位置,进行插入
            Node<E> child = new Node<>(element, parent);
            if (cmp < 0) {
                parent.leftChild = child;
            } else {
                parent.rightChild = child;
            }
            //平衡树出现问题,需要修正
            fixAfterInsertion(child);
            size++;
    
            return true;
        }
    
        /**
         * 修正插入之后的平衡树
         *
         * @param node
         */
        public void fixAfterInsertion(Node<E> node) {
            if (node == null) return;
    
            node.color = RED;
    
            while (node != null && node != root && node.parent.color == RED) {
                //1.父节点是祖父节点的左孩子,有3种情况
                if (parentOf(node) == leftOf(parentOf(parentOf(node)))) {
                    //取叔叔节点
                    Node<E> uncleNode = rightOf(parentOf(parentOf(node)));
                    if (colorOf(uncleNode) == RED) {//情况1:祖父节点的另一个子节点(叔叔节点)是红色
                        //对策: 将当前节点的父节点和叔叔节点涂黑,祖父节点涂红,把当前节点指向祖父节点,从新的当前节点开始算法
                        setColor(parentOf(node), BLACK);
                        setColor(uncleNode, BLACK);
                        setColor(parentOf(parentOf(node)), RED);
                        node = parentOf(parentOf(node));
                    } else {//情况2:叔叔节点是黑色
                        if (node == rightOf(parentOf(node))) {//情况2.1:当前节点是其父节点的右孩子
                            //对策:当前节点的父节点做为新的当前节点,以新当前节点为支点左旋。
                            node = parentOf(node);
                            leftRotate(node);
                        }
                        //情况2.2:当前节点是其父节点的左孩子
                        //对策:父节点变为黑色,祖父节点变红色,再祖父节点为支点进行右旋
                        setColor(parentOf(node), BLACK);
                        setColor(parentOf(parentOf(node)), RED);
                        rightRotate(parentOf(parentOf(node)));
                    }
                }
                //2.父节点是祖父节点的右孩子
                else {
                    //取叔叔节点
                    Node<E> uncleNode = leftOf(parentOf(parentOf(node)));
                    if (colorOf(uncleNode) == RED) {//情况1:祖父节点的另一个子节点(叔叔节点)是红色
                        //对策: 将当前节点的父节点和叔叔节点涂黑,祖父节点涂红,把当前节点指向祖父节点,从新的当前节点开始算法
                        setColor(parentOf(node), BLACK);
                        setColor(uncleNode, BLACK);
                        setColor(parentOf(parentOf(node)), RED);
                        node = parentOf(parentOf(node));
                    } else {//情况2:叔叔节点是黑色
                        if (node == leftOf(parentOf(node))) {//情况2.1:当前节点是其父节点的左孩子
                            //对策:当前节点的父节点做为新的当前节点,以新当前节点为支点右旋。
                            node = parentOf(node);
                            rightRotate(node);
                        }
                        //情况2.2:当前节点是其父节点的右孩子
                        //对策:父节点变为黑色,祖父节点变红色,再祖父节点为支点进行左旋
                        setColor(parentOf(node), BLACK);
                        setColor(parentOf(parentOf(node)), RED);
                        leftRotate(parentOf(parentOf(node)));
                    }
                }
            }
            root.color = BLACK;
        }
    
        /**
         * 根据当前的值,获取当前节点
         *
         * @param element
         * @return
         */
        public Node<E> getNode(E element) {
            if (element == null) {
                return null;
            }
            Node<E> t = root;
            Comparable<E> e = element;
            while (t != null) {
                int cmp = e.compareTo(t.element);
                if (cmp < 0) {
                    t = t.leftChild;
                } else if (cmp > 0) {
                    t = t.rightChild;
                } else {
                    return t;
                }
            }
            return null;
        }
    
        /**
         * 获取传入P节点下面最小的节点
         *
         * @param p
         * @return
         */
        public Node<E> getMinLeftNode(Node<E> p) {
            if (p == null) {
                return null;
            }
            Node<E> t = p;
            while (t.leftChild != null) {
                t = t.leftChild;
            }
            return t;
        }
    
        /**
         * 删除元素
         *
         * @param element
         */
        public void remove(E element) {
            //查找当前的节点
            Node<E> node = getNode(element);
            if (node == null) {
                return;
            }
            deleteElement(node);
        }
    
        /**
         * 删除当前的节点
         *
         * @param node
         */
        public void deleteElement(Node<E> node) {
    
            if (node.leftChild != null && node.rightChild != null) {
                Node<E> s = getMinLeftNode(node.rightChild);
                node.element = s.element;
                node = s;
            }
    
            Node<E> t = (node.leftChild != null ? node.leftChild : node.rightChild);
    
            if (t == null) {
                Node<E> parent = node.parent;
                if (parent == null) {
                    root = null;
                } else {
                    // 注重:如果删除节点是黑色,那么一定要先修正红黑二叉树,在做下面的删除动作
                    if (node.color == BLACK) {
                        fixAfterDeletion(node);
                    }
    
                    if (parent.leftChild == node) {
                        parent.leftChild = null;
                    } else {
                        parent.rightChild = null;
                    }
                    node.parent = null;
                }
            } else {
                t.parent = node.parent;
                if (node.parent == null) {
                    root = t;
                } else {
                    if (node.parent.leftChild == node) {
                        node.parent.leftChild = t;
                    } else {
                        node.parent.rightChild = t;
                    }
                }
                node.leftChild = node.rightChild = node.parent = null;
                if (node.color == BLACK) {
                    fixAfterDeletion(t);
                }
            }
    
            size--;
        }
    
        /**
         * 删除节点后,修正红黑树
         * @param node
         */
        public void fixAfterDeletion(Node<E> node) {
            while (node != root && colorOf(node) == BLACK) {
                if (leftOf(parentOf(node)) == node) { //被删除节点是父节点的左孩子
                    //拿到node的兄弟节点
                    Node<E> sib = rightOf(parentOf(node));
                    if (colorOf(sib) == RED) { //2.2 当前节点x的兄弟节点是红色(此时父节点和兄弟节点的子节点分为黑)
                        //对策:把父节点染成红色,兄弟节点染成黑色,对父节点进行左旋,重新设置x的兄弟节点
                        setColor(parentOf(node), RED);
                        setColor(sib, BLACK);
                        leftRotate(parentOf(node));
                        sib = rightOf(parentOf(node));
                    }
                    if (colorOf(sib) == BLACK) {//2.3 当前节点x 的兄弟节点是黑色
                        if (colorOf(leftOf(sib)) == BLACK && colorOf(rightOf(sib)) == BLACK) {//2.3.1 兄弟节点的两个孩子都是黑色
                            //对策:将x的兄弟节点设为红色,设置x的父节点为新的x节点
                            setColor(sib, RED);
                            node = parentOf(node);
                        } else {
                            if (colorOf(rightOf(sib)) == BLACK) {//2.3.2 兄弟的右孩子是黑色,左孩子是红色
                                //对策:将x兄弟节点的左孩子设为黑色,将x兄弟节点设置红色,将x的兄弟节点右旋,右旋后,重新设置x的兄弟节点。
                                setColor(leftOf(sib), BLACK);
                                setColor(sib, RED);
                                rightRotate(sib);
                                sib = rightOf(parentOf(node));
                            }
                            //2.3.3 兄弟节点的右孩子是红色
                            //对策:把兄弟节点染成当前节点父节点颜色,把当前节点父节点染成黑色,兄弟节点右孩子染成黑色,再以当前节点的父节点为支点进行左旋,算法结算
                            setColor(sib, colorOf(parentOf(node)));
                            setColor(parentOf(node), BLACK);
                            setColor(rightOf(sib), BLACK);
                            leftRotate(parentOf(node));
                            node = root;
                        }
                    }
                } else { //被删除节点是父节点的右孩子, 对策: 把上面的左设置为右
                    //拿到node的兄弟节点
                    Node<E> sib = leftOf(parentOf(node));
                    if (colorOf(sib) == RED) { //2.2 当前节点x的兄弟节点是红色(此时父节点和兄弟节点的子节点分为黑)
                        //对策:把父节点染成红色,兄弟节点染成黑色,对父节点进行右旋,重新设置x的兄弟节点
                        setColor(parentOf(node), RED);
                        setColor(sib, BLACK);
                        rightRotate(parentOf(node));
                        sib = leftOf(parentOf(node));
                    }
                    if (colorOf(sib) == BLACK) {//2.3 当前节点x 的兄弟节点是黑色
                        if (colorOf(leftOf(sib)) == BLACK && colorOf(rightOf(sib)) == BLACK) {//2.3.1 兄弟节点的两个孩子都是黑色
                            //对策:将x的兄弟节点设为红色,设置x的父节点为新的x节点
                            setColor(sib, RED);
                            node = parentOf(node);
                        } else {
                            if (colorOf(leftOf(sib)) == BLACK) {//2.3.2 兄弟的左孩子是黑色,右孩子是红色
                                //对策:将x兄弟节点的右孩子设为黑色,将x兄弟节点设置红色,将x的兄弟节点左旋,左旋后,重新设置x的兄弟节点。
                                setColor(rightOf(sib), BLACK);
                                setColor(sib, RED);
                                leftRotate(sib);
                                sib = leftOf(parentOf(node));
                            }
                            //2.3.3 兄弟节点的左孩子是红色
                            //对策:把兄弟节点染成当前节点父节点颜色,把当前节点父节点染成黑色,兄弟节点左孩子染成黑色,再以当前节点的父节点为支点进行右旋,算法结算
                            setColor(sib, colorOf(parentOf(node)));
                            setColor(parentOf(node), BLACK);
                            setColor(leftOf(sib), BLACK);
                            rightRotate(parentOf(node));
                            node = root;
                        }
                    }
                }
            }
        }
    
        /**
         * 一层一层打印出数据
         *
         * @param root)
         */
        public void showRBTree(Node<E> root) {
            if (root == null) {
                return;
            }
            LinkedList<Node<E>> list = new LinkedList<>();
            list.offer(root);
            while (!list.isEmpty()) {
                Node<E> node = list.pop();
                System.out.println("node.element = " + node.element + ", color = " + node.color);
                if (node.leftChild != null) {
                    list.offer(node.leftChild);
                }
                if (node.rightChild != null) {
                    list.offer(node.rightChild);
                }
            }
        }
    
        /**
         * 红黑树结点
         *
         * @param <E>
         */
        public class Node<E extends Comparable>{
            E element;
            Node<E> leftChild;
            Node<E> rightChild;
            Node<E> parent;
    
            //0:黑色  1:红色
            boolean color = BLACK;
    
            public Node(E element, Node<E> parent) {
                this.element = element;
                this.parent = parent;
            }
        }
    }
    

    5、测试

    @Test
    public void testRBTree(){
        Integer[] nums = {13,8,5,11,6,22,27,25,14,17};
        RBTree<Integer> tree = new RBTree<>();
        for (int i = 0; i < nums.length; i++) {
            Integer num = nums[i];
            tree.insertElement(num);
        }
        tree.showRBTree(tree.getRoot());
    
        tree.remove(25);
        System.out.println("---------------------------------------- ");
        tree.showRBTree(tree.getRoot());
    
        System.out.println("---------------------------------------- ");
        for (Integer num : nums) {
            tree.remove(num);
            tree.showRBTree(tree.getRoot());
            System.out.println("------------------------------------ : " + num);
        }
    
    }
    
    测试结果: true:是黑色,false:是红色
    ---------------打印插入数据--------------- 
    node.element = 13, color = true
    node.element = 8, color = false
    node.element = 25, color = false
    node.element = 5, color = true
    node.element = 11, color = true
    node.element = 17, color = true
    node.element = 27, color = true
    node.element = 6, color = false
    node.element = 14, color = false
    node.element = 22, color = false
    ---------------------------------------- 删除: 25
    node.element = 13, color = true
    node.element = 8, color = false
    node.element = 17, color = false
    node.element = 5, color = true
    node.element = 11, color = true
    node.element = 14, color = true
    node.element = 27, color = true
    node.element = 6, color = false
    node.element = 22, color = false
    ----------------循环删除----------------- 
    ------------------------------------ 删除: 13
    node.element = 14, color = true
    node.element = 8, color = false
    node.element = 22, color = false
    node.element = 5, color = true
    node.element = 11, color = true
    node.element = 17, color = true
    node.element = 27, color = true
    node.element = 6, color = false
    ------------------------------------ 删除: 8
    node.element = 14, color = true
    node.element = 6, color = false
    node.element = 22, color = false
    node.element = 5, color = true
    node.element = 11, color = true
    node.element = 17, color = true
    node.element = 27, color = true
    ------------------------------------ 删除: 5
    node.element = 14, color = true
    node.element = 6, color = false
    node.element = 22, color = false
    node.element = 11, color = false
    node.element = 17, color = true
    node.element = 27, color = true
    ------------------------------------ 删除: 11
    node.element = 14, color = true
    node.element = 6, color = false
    node.element = 22, color = false
    node.element = 17, color = true
    node.element = 27, color = true
    ------------------------------------ 删除: 6
    node.element = 14, color = true
    node.element = 22, color = false
    node.element = 17, color = true
    node.element = 27, color = true
    ------------------------------------ 删除: 22
    node.element = 14, color = true
    node.element = 27, color = false
    node.element = 17, color = false
    ------------------------------------ 删除: 27
    node.element = 14, color = true
    node.element = 17, color = false
    ------------------------------------ 删除: 25
    node.element = 14, color = true
    node.element = 17, color = false
    ------------------------------------ 删除: 14
    node.element = 17, color = false
    ------------------------------------ 删除: 17
    

    附上插入数据的红黑树 笔记 如图:

    9.jpg

    相关文章

      网友评论

          本文标题:手写红黑树,实现增、删、查功能

          本文链接:https://www.haomeiwen.com/subject/qewwkqtx.html