红黑树的旋转、插入、删除

258 阅读9分钟

一、定义: 一种自平衡二叉查找树,它可以在O(logn)时间内完成查找、插入和删除,这里的n是树中元素的数目。

二、性质:

  1. 节点是红色或黑色
  2. 根节点是黑色
  3. 所有叶子都是黑色(叶子是NIL节点)
  4. 每个红色节点必须有两个黑色的子节点。(或者说从每个叶子到根的所有路径上不能有两个连续的红色节点。)(或者说不存在两个相邻的红色节点,相邻指两个节点是父子关系。)(或者说红色节点的父节点和子节点均是黑色的。)
  5. 从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点 (一个推论:如果一个节点存在黑子节点,那么该节点一定有两个子节点)

三、旋转:

  • 左旋:以某个节点作为支点(旋转节点),其右子节点变为旋转节点的父节点,右子节点的左子节点变为旋转节点的右节点
  • 右旋:以某个节点作为支点(旋转节点),其左子节点变为旋转节点的父节点,左子节点的右子节点变为旋转节点的左子节点

Tree_rotation_animation_250x250.gif

四、插入:

WechatIMG9.png

  1. 为了插入之后不破坏平衡(黑高),新插入的节点必须为红色。

  2. 当前红黑树为空树时,直接把插入节点设为根,并且将红色变为黑色即可。

  3. 插入节点的父节点为红色时:

    • (1)叔叔节点为红色: 这种情况将父亲和叔叔节点变为黑色,爷爷节点变为红色,回溯到爷爷节点重复进行上述操作即可。
    • (2)叔叔节点不存在或者为黑节点时, 此时可以分为四种:
      • LL双红:父亲节点变黑,爷爷节点变红,对爷爷节点右旋。(RR双红旋转方向相反即可) WechatIMG5.png
      • LR双红:先对父亲节点左旋,然后转变为LL双红的情况同上述处理即可。 WechatIMG6.png
      • RR双红:同LL双红处理,将右旋变为左旋即可。 WechatIMG7.png
      • RL双红:先对父节点右旋,转变为RR双红的情况同上述处理即可。 WechatIMG8.png
  4. 插入节点的父节点为黑色时:直接插入即可。

五、删除:删除流程见下图

总共可以分为下面A B C D四种情况: WechatIMG10.png

  • 上述中C和D最终都可以转化A和B,对于A可以直接删除,B的话又会分为下面四种情况:

WechatIMG12.png (注意:在上述第(1)种情况中,指针回溯遇到根节点和红节点时直接变黑就结束了,否则就继续递归) (注意:在上述第(4)种情况中,经过变换调整后需要继续对当前要删除的节点循环调整

六、手写红黑树源码:


import java.util.ArrayList;
import java.util.List;

public class RBtree<K extends Comparable<K>, V> {
    private static final boolean RED = true;
    private static final boolean BLACK = false;
    private RBNode<K, V> root;


    static class RBNode<K extends Comparable<K>, V> {
        private boolean color;
        private RBNode<K, V> parent;
        private RBNode<K, V> left;
        private RBNode<K, V> right;
        private K key;
        private V value;

        public RBNode() {
        }

        public RBNode(boolean color, RBNode<K, V> parent, RBNode<K, V> left, RBNode<K, V> right, K key, V value) {
            this.color = color;
            this.parent = parent;
            this.left = left;
            this.right = right;
            this.key = key;
            this.value = value;
        }

        public boolean getColor() {
            return color;
        }

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

        public RBNode<K, V> getParent() {
            return parent;
        }

        public void setParent(RBNode<K, V> parent) {
            this.parent = parent;
        }

        public RBNode<K, V> getLeft() {
            return left;
        }

        public void setLeft(RBNode<K, V> left) {
            this.left = left;
        }

        public RBNode<K, V> getRight() {
            return right;
        }

        public void setRight(RBNode<K, V> right) {
            this.right = right;
        }

        public K getKey() {
            return key;
        }

        public void setKey(K key) {
            this.key = key;
        }

        public V getValue() {
            return value;
        }

        public void setValue(V value) {
            this.value = value;
        }


    }


    /**
     * 获取当前节点的父节点
     *
     * @param node
     * @return
     */
    private RBNode<K, V> parentOf(RBNode<K, V> node) {
        if (node != null) return node.parent;
        return null;
    }

    private boolean isRed(RBNode<K, V> node) {
        if (node != null) {
            return node.color == RED;
        }
        return false;
    }

    private boolean isBlack(RBNode<K, V> node) {
        if (node != null) {
            return node.color == BLACK;
        }
        return true; // null节点视为黑色
    }

    private void setRed(RBNode<K, V> node) {
        if (node != null) {
            node.setColor(RED);
        }
    }

    private void setBlack(RBNode<K, V> node) {
        if (node != null) {
            node.setColor(BLACK);
        }
    }


    public void inOrderPrint() {
        inOrderPrint(this.root);
    }

    /**
     * 中序打印
     *
     * @param node
     */
    private void inOrderPrint(RBNode<K, V> node) {
        if (node != null) {
            inOrderPrint(node.left);
            System.out.println("key:" + node.key + ",value:" + node.value);
            inOrderPrint(node.right);
        }
    }


    public RBNode<K, V> getRoot() {
        return root;
    }


    /**
     * 左旋
     *       p                           p
     *       |                           |
     *       x                           y
     *      / \      ----->             / \
     *    lx   y                       x   ry
     *        / \                     / \
     *      ly   ry                 lx   ly
     */
    private void leftrotate(RBNode<K, V> x) {
        if (x == null) return;
        RBNode<K, V> y = x.right;
        if (y == null) return;
        //1、将x的右节点指向y的左子节点ly,将ly的父节点指向x
        x.right = y.left;
        if (y.left != null) {
            y.left.parent = x;
        }

        // 2、将x和其父节点p的关系变为y和x的父节点的关系
        y.parent = x.parent;
        if (x.parent == null) {
            this.root = y;
        } else {
            if (x.parent.left == x) {
                x.parent.left = y;
            } else {
                x.parent.right = y;
            }
        }

        //3、将x的父节点指向y,y的左节点指向x
        x.parent = y;
        y.left = x;

    }

    /**
     * 右旋
     */
    private void rightrotate(RBNode<K, V> x) {
        if (x == null) return;
        RBNode<K, V> y = x.left;
        if (y == null) return;
        x.left = y.right;
        if (y.right != null) {
            y.right.parent = x;
        }

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

        x.parent = y;
        y.right = x;

    }


    /**
     * 对外公开的插入方法
     */
    public void insert(K key, V value) {
        RBNode<K, V> node = new RBNode<>();
        node.setKey(key);
        node.setValue(value);
        //新节点必须设为红色
        node.setColor(RED);
        insert(node);
    }

    private void insert(RBNode<K, V> node) {
        if (this.root == null) {//如果为空树,则直接将插入的节点变为根节点,并将颜色变为黑色
            node.setColor(BLACK);
            this.root = node;
            return;
        }
        RBNode<K, V> x = this.root;
        RBNode<K, V> parent = x;
        while (x != null) {
            parent = x;
            //cmp > 0 ,说明key大于 x.key 需要到x的右子树查找
            //cmp < 0 ,说明key小于 x.key 需要到x的左子树查找
            //cmp == 0,说明key等于 x.需要进行替换操作
            int cmp = node.key.compareTo(x.key);
            if (cmp > 0) {
                x = x.right;
            } else if (cmp < 0) {
                x = x.left;
            } else {
                x.value = node.value;
                return;//找到相同key时应该return
            }
        }
        node.parent = parent;
        int cmp = node.key.compareTo(parent.key);
        if (cmp > 0) {
            parent.right = node;
        } else {
            parent.left = node;
        }

        //插入有可能破坏平衡,下面进行修复平衡
        insertFixup(node);

    }

    /**
     * 修复由于插入导致的不平衡
     *
     * @param node
     */
    private void insertFixup(RBNode<K, V> node) {
        if (node == null) return;
        RBNode<K, V> parent = parentOf(node);
        if (parent == null || root == node) {//当前节点为根节点,直接设置为黑色即可
            node.color = BLACK;
            return;
        }
        //父亲节点为黑节点时不需要处理
        if (isBlack(parent)) return;

        RBNode<K, V> grandF = parentOf(parent);
        if (grandF == null) return;

        //父亲叔叔都为红色,父叔变黑,爷爷变红,回溯到爷爷节点继续上述处理
        if (isRed(grandF.right) && isRed(grandF.left)) {
            setBlack(grandF.left);
            setBlack(grandF.right);
            setRed(grandF);
            insertFixup(grandF);
            return;
        }

        //叔叔节点不存在或者为黑节点时,分为四种情况:
        if (grandF.right == null || isBlack(grandF.right)) {

            if (parent.left == node) {
                //LL双红:父节点变黑,爷节点变红,爷节点右旋
                setBlack(parent);
                setRed(grandF);
                rightrotate(grandF);

            } else {
                //LR双红:先对父节点左旋,然后再按照LL双红处理
                leftrotate(parent);
                setBlack(node);
                setRed(grandF);
                rightrotate(grandF);
            }
        } else if (grandF.left == null || isBlack(grandF.left)) {
            if (parent.right == node) {
                //RR双红 :父节点变黑,爷节点变红,爷节点左旋
                setBlack(parent);
                setRed(grandF);
                leftrotate(grandF);
            } else {
                //RL双红:先对父节点右旋,然后再按照RR双红处理
                rightrotate(parent);
                setBlack(node);
                setRed(grandF);
                leftrotate(grandF);
            }
        }

    }


    /**
     * 找结点(x)的后继结点。即,查找"红黑树中数据值大于该结点"的"最小结点"。
     */
    public RBNode<K, V> findSuccessor(RBNode<K, V> node) {
        if (node.right != null) {
            return findMin(node.right);
        } else {
            RBNode<K, V> parent = node.parent;
            while (parent != null && node == parent.right) {
                node = parent;
                parent = parent.parent;
            }
            return parent;
        }
    }


    /**
     * 找结点(x)的前驱结点。即,查找"红黑树中数据值小于该结点"的"最小结点"。
     */
    public RBNode<K, V> findPredecessor(RBNode<K, V> node) {
        if (node.left != null) {
            return findMax(node.left);
        } else {
            RBNode<K, V> parent = node.parent;
            while (parent != null && node == parent.left) {
                node = parent;
                parent = parent.parent;
            }
            return parent;
        }
    }


    /**
     * 查找最小节点
     *
     * @param node
     * @return
     */
    private RBNode<K, V> findMin(RBNode<K, V> node) {
        if (node == null) return null;
        while (node.left != null) {
            node = node.left;
        }
        return node;
    }


    /**
     * 查找最大节点
     *
     * @param node
     * @return
     */
    private RBNode<K, V> findMax(RBNode<K, V> node) {
        if (node == null) return null;
        while (node.right != null) {
            node = node.right;
        }
        return node;
    }


    /**
     * 对外公开的删除方法
     */
    public void deleteNode(K key) {
        deleteNode(root, key);
    }

    private void deleteNode(RBNode<K, V> root, K key) {
        RBNode<K, V> node = findNodeRec(root, key);
        if (node == null) return;
        if (node.left != null && node.right != null) {//有两个子节点
            RBNode<K, V> successorNode = findSuccessor(node);
            node.key = successorNode.key;
            node.value = successorNode.value;
            node = successorNode;
        }

        if (node.left == null && node.right == null) {//没有子节点
            if (node.color == RED) {
                clearNode(node);
            } else {
                if (node.parent == null) {
                    node = null;
                    this.root = node;
                } else {
                    //黑节点平衡处理
                    deleteFix(node);
                    clearNode(node);
                }
            }
        } else {//只有一个子节点
            RBNode<K, V> child = node.left != null ? node.left : node.right;
            child.color = BLACK;
            if (node.parent == null) {//说明node节点是根节点
                this.root = child;
                child.parent = null;
            } else {
                child.parent = node.parent;
                if (node == node.parent.left) {
                    node.parent.left = child;
                } else {
                    node.parent.right = child;
                }
            }

        }
    }


    /**
     * 删除后平衡操作
     *
     * @param node
     */
    private void deleteFix(RBNode<K, V> node) {
        RBNode<K, V> pp = parentOf(node);
        if (pp == null) return; //根节点,无需处理
        if (node == pp.left) {
            RBNode<K, V> ppr = pp.right;
            //注意这里ppr可能为null
            if (ppr != null && ppr.color == RED) {                   /** (4) **/
                //兄弟为红则:父变红,兄弟变黑,左旋父,继续递归
                pp.color = RED;
                ppr.color = BLACK;
                leftrotate(pp);
                deleteFix(node);
            } else if (ppr.right != null && ppr.right.color == RED) { /** (2) **/
                //兄弟右孩子(远端)为红:(兄弟)以父为名,父亲变黑,孩子变黑,左旋父
                ppr.color = pp.color;
                pp.color = BLACK;
                ppr.right.color = BLACK;
                leftrotate(pp);
            } else if (ppr.left != null && ppr.left.color == RED) {   /** (3) **/
                //兄弟左孩子为红:兄弟变红,兄弟孩子变黑,右旋兄弟,回到情况(2)
                ppr.color = RED;
                ppr.left.color = BLACK;
                rightrotate(ppr);


                //-----回到情况(2)---------------------------
                ppr = pp.right;//(注意:旋转后需要更新ppr)

                ppr.color = pp.color;
                pp.color = BLACK;
                ppr.right.color = BLACK;
                leftrotate(pp);

            } else {                                                  /** (1) **/
                //兄弟和兄弟子节点全部为黑:兄弟变红,指针回溯到父亲节点继续递归(节点上移,如果碰到根节点直接停止,如果碰到红节点则变黑即可)
                ppr.color = RED;
                if (parentOf(pp) != null && pp != root) {
                    if (pp.color == RED) {//碰到红节点,直接变黑
                        pp.color = BLACK;
                    } else {
                        deleteFix(pp);//继续递归
                    }
                } else {
                    //碰到根节点,直接停止即可
                }

            }
        } else {
            RBNode<K, V> ppl = pp.left;

            if (ppl.color == RED) {                                  /** (4) **/
                //兄弟为红则:父变红,兄弟变黑,右旋父,继续递归
                pp.color = RED;
                ppl.color = BLACK;
                rightrotate(pp);
                deleteFix(node);
            } else if (ppl.left != null && ppl.left.color == RED) {  /** (2) **/
                //兄弟左孩子(远端)为红:(兄弟)以父为名,父亲变黑,孩子变黑,右旋父
                ppl.color = pp.color;
                pp.color = BLACK;
                ppl.left.color = BLACK;
                rightrotate(pp);

            } else if (ppl.right != null && ppl.right.color == RED) { /** (3) **/
                //兄弟右孩子为红:兄弟变红,兄弟孩子变黑,左旋兄弟,回到情况(2)
                ppl.color = RED;
                ppl.right.color = BLACK;
                leftrotate(ppl);


                //-----回到情况(2)---------------------------
                ppl = pp.left;//(注意:旋转后需要更新ppl)

                ppl.color = pp.color;
                pp.color = BLACK;
                ppl.left.color = BLACK;
                rightrotate(pp);

            } else {                                                  /** (1) **/
                //兄弟和兄弟子节点全部为黑:兄弟变红,指针回溯到父亲节点继续递归(节点上移,如果碰到根节点直接停止,如果碰到红节点则变黑即可)
                ppl.color = RED;
                if (pp.parent != null && pp != root) {
                    if (pp.color == RED) {//碰到红节点,直接变黑
                        pp.color = BLACK;
                    } else {
                        deleteFix(pp);//继续递归
                    }
                } else {
                    //碰到根节点,直接停止即可
                }

            }
        }

    }

    /**
     * 删除当前节点
     *
     * @param node
     */
    private void clearNode(RBNode<K, V> node) {
        RBNode<K, V> parent = node.parent;
        if (parent != null) {
            if (node == parent.left) {
                parent.left = null;
            } else {
                parent.right = null;
            }
        } else {
            // 如果是根节点,需要更新root引用
            this.root = null;
        }

        // 清理节点自身的引用,避免内存泄漏
        node.parent = null;
        node.left = null;
        node.right = null;
        node.key = null;
        node.value = null;
    }


    /**
     * 查找指定节点
     *
     * @param root
     * @param key
     * @return
     */
    private RBNode<K, V> findNodeRec(RBNode<K, V> root, K key) {
        if (root == null || key == null) return null;
        int cmp = key.compareTo(root.key);
        if (cmp < 0) {
            return findNodeRec(root.left, key);
        } else if (cmp > 0) {
            return findNodeRec(root.right, key);
        } else {
            return root;
        }
    }


    //---------------------------------------------------------------------------------------------------------------------------

    /**
     * 下面是辅助打印的相关代码,可以不用管
     **/
    //---------------------------------------------------------------------------------------------------------------------------
    public void printTree(RBtree<K, V> tree) {
        RBNode<K, V> root = tree.getRoot();
        if (root == null) {
            System.out.println("Tree is empty");
            return;
        }

        List<List<String>> lines = new ArrayList<>();
        List<RBNode<K, V>> level = new ArrayList<>();
        List<RBNode<K, V>> next = new ArrayList<>();

        level.add(root);
        int nn = 1;
        int widest = 0;

        while (nn != 0) {
            List<String> line = new ArrayList<>();

            nn = 0;

            for (RBNode<K, V> n : level) {
                if (n == null) {
                    line.add(null);

                    next.add(null);
                    next.add(null);
                } else {
                    String aa = n.key + "(" + (n.color == RED ? "R" : "B") + ")";
                    line.add(aa);
                    if (aa.length() > widest) widest = aa.length();

                    next.add(n.left);
                    next.add(n.right);

                    if (n.left != null) nn++;
                    if (n.right != null) nn++;
                }
            }

            if (widest % 2 == 1) widest++;

            lines.add(line);

            List<RBNode<K, V>> tmp = level;
            level = next;
            next = tmp;
            next.clear();
        }

        int perPiece = lines.get(lines.size() - 1).size() * (widest + 4);
        for (int i = 0; i < lines.size(); i++) {
            List<String> line = lines.get(i);
            int hpw = (int) Math.floor(perPiece / 2f) - 1;

            if (i > 0) {
                for (int j = 0; j < line.size(); j++) {

                    // split node
                    char c = ' ';
                    if (j % 2 == 1) {
                        if (line.get(j - 1) != null) {
                            c = (line.get(j) != null) ? '┴' : '┘';
                        } else {
                            if (j < line.size() && line.get(j) != null) c = '└';
                        }
                    }
                    System.out.print(c);

                    // lines and spaces
                    if (line.get(j) == null) {
                        for (int k = 0; k < perPiece - 1; k++) {
                            System.out.print(" ");
                        }
                    } else {

                        for (int k = 0; k < hpw; k++) {
                            System.out.print(j % 2 == 0 ? " " : "─");
                        }
                        System.out.print(j % 2 == 0 ? "┌" : "┐");
                        for (int k = 0; k < hpw; k++) {
                            System.out.print(j % 2 == 0 ? "─" : " ");
                        }
                    }
                }
                System.out.println();
            }

            for (int j = 0; j < line.size(); j++) {

                String f = line.get(j);
                if (f == null) f = "";
                int gap1 = (int) Math.ceil(perPiece / 2f - f.length() / 2f);
                int gap2 = (int) Math.floor(perPiece / 2f - f.length() / 2f);

                // a node
                for (int k = 0; k < gap1; k++) {
                    System.out.print(" ");
                }
                System.out.print(f);
                for (int k = 0; k < gap2; k++) {
                    System.out.print(" ");
                }
            }
            System.out.println();

            perPiece /= 2;
        }
    }
}