红黑树杀人事件始末

前言

红黑树是算法领域中一个著名的二叉查找树实现,它能够以较小的开销保持二叉查找树的平衡。具备平衡性质的二叉查找树能够极大地提高节点的查询速度。举个形象一点的例子:从一个十亿节点的红黑树中查找一个节点,所需要的查询次数不到30,这不禁让人感叹算法的魅力。

红黑树是工程中最常见的二叉查找树的实现,例如在Linux的内存管理和进程管理中就用到了红黑树;Java语言的集合包、C++语言的标准模板库中均提供了红黑树的实现类。

红黑树本身的设计很复杂,多数情况下我们也不需要自己去实现红黑树,但研究红黑树还是有意义的。一方面可以让学习者领略这种神奇的数据结构的奥妙,另一方面可以作为一种思维训练工具,提升自己的算法设计能力。

本文以漫画形式讲述红黑树,第一话讲解二叉查找树的概念和基本操作,包括节点查找、插入、删除;第二话讲解二叉查找树的缺点和红黑树的概念,以及红黑树的节点旋转操作;第三话讲解红黑树的节点插入操作;第四话讲解红黑树的节点删除操作;第五话和彩蛋部分讲解红黑树在插入修正和删除修正时,对各种CASE所进行的调整操作背后的思想。

红黑树的实现中,最难的部分在于实现节点的插入和删除时,要穷举所有可能的CASE,然后对每种CASE进行处理。在理解节点的插入和删除的过程时,读者要把握住一个中心:每种CASE所进行的调整步骤都在尽量恢复插入/删除节点后所违反的红黑树特性,如果当前CASE解决不了,就转成另一种更接近问题解决状态的CASE。每种CASE的所进行的调整步骤都是为了向解决问题的出口更靠近一步,直至找到出口。

漫画采用大量的图示来展示红黑树操作的诸多细节,作者力求详尽,因此篇幅略长。附录给出了完整的二叉查找树定义 + 测试代码,以及红黑树定义 + 测试代码。两份代码均经过了若干次十万数量级随机节点插入和删除测试,结果均无误。读者可贴到自己的 IDE 中,结合本文讲解进行调试研究。

在电脑端本文阅读效果更佳。另外,读者可联系「码海」公众号博主(微信:geekoftaste)获取本漫画的pdf版本。

正文

附录:代码清单

二叉查找树代码

BsTreeTest.java

package bst;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class BsTreeTest {
    public static void main(String[] args) {

        // 随机数的上限
        int bound = 10000000;

        // 随机序列的长度
        int seqLen = 100000;

        // 随机数生成器
        Random random = new Random();

        // 根节点值
        int rootValue = random.nextInt(bound);

        // 随机序列容器
        List<Integer> intSeq = new ArrayList<>();
        intSeq.add(rootValue);

        // 往随机序列容器中添加随机值
        while (intSeq.size() < seqLen) {
            int rand = random.nextInt(bound);
            intSeq.add(rand);
        }

        System.out.println("插入序列:" + intSeq);

        // 创建一个bst
        BsTree<Integer> bsTree = new BsTree<>();
        BsTreeNode<Integer> root = new BsTreeNode<>(rootValue);

        // 把intSeq中的随机数全部插入进去,测试节点插入过程
        for (Integer key : intSeq) {
            bsTree.insertRecursively(root, new BsTreeNode<Integer>(key));
        }

        // 插入完成后,打印整个bst的中序遍历结果,验证是否符合二叉搜索树的特性
        List<Integer> middleOderOutput1 = new LinkedList<>();
        bsTree.middleOder(root, middleOderOutput1);
        System.out.println("构造完成,中序遍历结果为:" + middleOderOutput1);
        System.out.println();

        // 选取一部分节点值,测试节点删除过程
        List<Integer> delList = middleOderOutput1.subList(middleOderOutput1.size() / 4, middleOderOutput1.size() / 2);

        for (int item : delList) {
            bsTree.deleteRecursively(root, item);
        }

        List<Integer> middleOderOutput2 = new LinkedList<>();
        bsTree.middleOder(root, middleOderOutput2);
        System.out.println(" 删除完成,中序遍历结果为:" + middleOderOutput2);
        System.out.println();

        // 测试bst中“删除前总节点数目 - 删除节点数目 = 删除后总节点数目”是否成立
        System.out.println(" (out1.size()- out2.size()) == delList.size()? " + ((middleOderOutput1.size() - middleOderOutput2.size()) == delList.size()));
        // 测试bst中“删除前节点  包含全部  删除后节点”是否成立
        System.out.println("out1.containsAll(out2)? " + (middleOderOutput1.containsAll(middleOderOutput2)));
        // 测试bst中“删除前节点  包含全部  被删除节点”是否成立
        System.out.println("out1.containsAll(delList)? " + (middleOderOutput1.containsAll(delList)));
    }
}

BsTree.java

package bst;

import java.util.Collection;

public class BsTree<T extends Comparable<T>> {

    /**
     * 使用递归实现 BsTree 查找
     *
     * @param root BST 根节点引用
     * @param key  待查找的节点值
     * @return 命中的节点
     */

    public BsTreeNode<T> searchRecursively(BsTreeNode<T> root, T key) {
        if (root == null) {
            return null;
        }

        if (root.nodeKey.compareTo(key) > 0) {
            return searchRecursively(root.left, key);
        } else if (root.nodeKey.compareTo(key) < 0) {
            return searchRecursively(root.right, key);
        } else {
            return root;
        }
    }

    /**
     * 中序遍历 BsTree,打印节点值(递归实现)
     *
     * @param root
     */

    public void inOder(BsTreeNode<T> root) {
        if (root == null) {
            return;
        }
        inOder(root.left);
        System.out.print("->" + root.nodeKey.toString());
        inOder(root.right);
    }

    /**
     * 中序遍历 BsTree,打印节点值(递归实现)
     *
     * @param root
     */

    public void middleOder(BsTreeNode<T> root, Collection<T> collection) {
        if (root == null) {
            return;
        }

        middleOder(root.left, collection);
        //System.out.print("->" + root.nodeKey.toString());
        collection.add(root.nodeKey);
        middleOder(root.right, collection);
    }

    /**
     * 向 BsTree 中插入节点(递归实现)
     * <p>
     * 二叉排序树本身是动态查找表的一种表示形式,有时会在
     * 查找过程中插入或者删除表中元素。当因为查找失败而需
     * 要插入数据元素时,该数据元素的插入位置一定位于二叉
     * 排序树的叶子结点,并且一定是查找失败时访问的最后一
     * 个结点的左孩子或者右孩子。
     *
     * @param root BsTree 根节点引用
     * @param key  待插入的节点值
     * @return 插入成功返回 true,如果树中有该元素不需要插入则返回 false
     */

    public boolean insertRecursively(BsTreeNode<T> root, T key) {
        if (root.nodeKey.compareTo(key) > 0) {
            if (root.left == null) {
                BsTreeNode<T> node = new BsTreeNode(key);
                root.left = node;
                node.parent = root;
                return true;
            } else {
                return insertRecursively(root.left, key);
            }
        } else if (root.nodeKey.compareTo(key) < 0) {
            if (root.right == null) {
                BsTreeNode<T> node = new BsTreeNode(key);
                root.right = node;
                node.parent = root;
                return true;
            } else {
                return insertRecursively(root.right, key);
            }
        } else {
            return false;
        }
    }

    /**
     * 向 BsTree 中插入节点(递归实现)
     * <p>
     * 二叉排序树本身是动态查找表的一种表示形式,有时会在
     * 查找过程中插入或者删除表中元素。当因为查找失败而需
     * 要插入数据元素时,该数据元素的插入位置一定位于二叉
     * 排序树的叶子结点,并且一定是查找失败时访问的最后一
     * 个结点的左孩子或者右孩子。
     *
     * @param root         BsTree 根节点引用
     * @param nodeInserted 待插入的节点值
     * @return 插入成功返回 true,如果树中有该元素不需要插入则返回 false
     */

    public boolean insertRecursively(BsTreeNode<T> root,
                                     BsTreeNode<T> nodeInserted)
 
{
        if (root == null) {
            root = nodeInserted;
            return true;
        }

        if (root.nodeKey.compareTo(nodeInserted.nodeKey) > 0) {
            if (root.left == null) {
                root.left = nodeInserted;
                nodeInserted.parent = root;
                return true;
            } else {
                return insertRecursively(root.left, nodeInserted);
            }
        } else if (root.nodeKey.compareTo(nodeInserted.nodeKey) < 0) {
            if (root.right == null) {
                root.right = nodeInserted;
                nodeInserted.parent = root;
                return true;
            } else {
                return insertRecursively(root.right, nodeInserted);
            }
        } else {
            return false;
        }
    }


    /**
     * 假设要删除的为结点 p,则对于二叉排序树来说,需要根据结点 p
     * 所在不同的位置作不同的操作,有以下 3 种可能:
     * 1. 结点 p 为叶子结点,此时只需要删除该结点,并修改其双亲结点的指针即可
     * 2. 结点 p 只有左子树或者只有右子树,如果 p 是其双亲节点的左孩子,则直
     * ---接将 p 节点的左子树或右子树作为其双亲节点的左子树;反之也是如此,如
     * ---果 p 是其双亲节点的右孩子,则直接将 p 节点的左子树或右子树作为其双
     * ---亲节点的右子树;
     * 3. 结点 p 左右子树都有,此时有两种处理方式:
     * ---3.1. 令结点 p 的左子树为其双亲结点的左子树;结点 p 的右子树为其自
     * --------身直接前驱结点的右子树
     * ---3.2. 用结点 p 的直接前驱(或直接后继)来代替结点 p,同时在二叉排序
     * --------树中对其直接前驱(或直接后继)做删除操作(ps:这种方式更好,因
     * --------为这样可以最大程度的保证原树的结构,而且不会让树过于倾斜)
     */

    public void deleteRecursively(BsTreeNode<T> currentNode, T key) {
        if (currentNode == null) {
            return;
        }

        // 当前节点键值大于 key,去左子树寻找
        if (currentNode.nodeKey.compareTo(key) > 0) {
            deleteRecursively(currentNode.left, key);
        }
        // 当前节点键值小于 key,去右子树寻找
        else if (currentNode.nodeKey.compareTo(key) < 0) {
            deleteRecursively(currentNode.right, key);
        }
        // 当前节点键值等于 key,处理当前节点
        else {
            // 对于第一种情形,左右子树均为空,直接删掉就是了,不涉及树结构的调整
            if (currentNode.right == null && currentNode.left == null) {
                BsTreeNode<T> parent = currentNode.parent;

                // 通过父节点来删除节点
                if (parent.left == currentNode) {
                    parent.left = null;
                }
                if (parent.right == currentNode) {
                    parent.right = null;
                }

                System.out.print(" case1 delete: " + key);

                return;
            }
            // 对于第二种情形,待删除节点只有左子树,用待删除节点的左子树替换待删除节点即可
            else if (currentNode.right == null) {
                BsTreeNode<T> rootLeft = currentNode.left;
                T temp = rootLeft.nodeKey;
                currentNode.left = rootLeft.left;
                currentNode.right = rootLeft.right;
                currentNode.nodeKey = temp;

                // 修正parent引用
                if (currentNode.left != null) {
                    currentNode.left.parent = currentNode;
                }
                if (currentNode.right != null) {
                    currentNode.right.parent = currentNode;
                }
                System.out.print(" case2 delete: " + key);
            }
            // 对于第三种情形,待删除节点只有右子树,用待删除节点的右子树替换待删除节点即可
            else if (currentNode.left == null) {
                BsTreeNode<T> rootRight = currentNode.right;
                T temp = rootRight.nodeKey;
                currentNode.left = rootRight.left;
                currentNode.right = rootRight.right;
                currentNode.nodeKey = temp;

                // 修正parent引用
                if (currentNode.left != null) {
                    currentNode.left.parent = currentNode;
                }
                if (currentNode.right != null) {
                    currentNode.right.parent = currentNode;
                }
                System.out.print(" case3 delete: " + key);
            }

            /**
             * 第四种情形,左右子树都不为空,用待删除节点的直接前驱(或直接后继)
             * 来代替待删除节点,同时在二叉排序树中对其直接前驱(或直接后继)做
             * 删除操作
             */

            else {
                // 找到当前节点的的中序前驱节点
                BsTreeNode<T> node = currentNode.left;
                while (node.right != null) {
                    node = node.right;
                }

                // 节点内容替换
                currentNode.nodeKey = node.nodeKey;
                // 删除前驱节点
                deleteRecursively(node, node.nodeKey);

                System.out.print(" case4 delete: " + key);
            }
        }
    }


}


class BsTreeNode<T extends Comparable<T>> {
    /**
     * 关键字(键值)
     */

    T nodeKey;

    /**
     * 左节点引用
     */

    BsTreeNode<T> left;

    /**
     * 右节点引用
     */

    BsTreeNode<T> right;

    /**
     * 父节点引用,进行节点删除的时候会用到
     */

    BsTreeNode<T> parent;

    public BsTreeNode(T value) {
        this.nodeKey = value;
    }

    public BsTreeNode() {
    }

    @Override
    public String toString() {
        return nodeKey.toString();
    }
}

红黑树代码

RbTreeTest.java

package rbt;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class RbTreeTest {

    public static void main(String[] args) {

        // 随机数的上限
        int bound = 1000000000;

        // 随机序列的长度
        int seqLen = 100000;

        // 随机数生成器
        Random random = new Random();

        // 根节点值
        int rootValue = random.nextInt(bound);

        // 随机序列容器
        List<Integer> intSeq = new ArrayList<>();
        intSeq.add(rootValue);

        // 往随机序列容器中添加随机值
        while (intSeq.size() < seqLen) {
            int rand = random.nextInt(bound);
            intSeq.add(rand);
        }

        System.out.println("压测开始,节点数量:" + seqLen);

        System.out.println("插入序列:" + intSeq);

        // 创建一个rbt
        RbTree<Integer> rbTree = new RbTree<>();

        // 把intSeq中的随机数全部插入进去,测试节点插入过程
        for (Integer key : intSeq) {
            rbTree.insert(key);
        }

        // 插入完成后,打印整个rbt的中序遍历结果,验证是否符合二叉搜索树的特性
        List<Integer> middleOderOutput1 = new LinkedList<>();
        rbTree.inOrder(middleOderOutput1);
        System.out.println(" 插入完成,中序遍历结果为:" + middleOderOutput1);
        System.out.println();

        // 打印整个rbt的形状
        rbTree.printTree();
        System.out.println();

        // 打印根节点到每个叶子节点的路径中,黑节点的数量,验证bst是否符合黑节点完美平衡特性
        List<RbTreeNode<Integer>> linkedList = new LinkedList<>();
        System.out.println();
        rbTree.printAllRootToLeafPaths(linkedList);
        System.out.println();

        // 选取一部分节点值,测试节点删除过程
        List<Integer> delList = middleOderOutput1.subList(middleOderOutput1.size() / 4, middleOderOutput1.size() / 2);
        System.out.println(" 删除序列:" + delList);

        for (Integer key : delList) {
            rbTree.remove(key);
        }

        List<Integer> middleOderOutput2 = new LinkedList<>();
        rbTree.inOrder(middleOderOutput2);
        System.out.println(" 删除完成,中序遍历结果为:" + middleOderOutput2);
        System.out.println();

        // 打印整个rbt的形状
        rbTree.printTree();
        System.out.println();

        // 打印根节点到每个叶子节点的路径中,黑节点的数量,验证bst是否符合黑节点完美平衡特性
        List<RbTreeNode<Integer>> linkedList2 = new LinkedList<>();
        System.out.println();
        rbTree.printAllRootToLeafPaths(linkedList2);
        System.out.println();

        // 测试rbt中“删除前总节点数目 - 删除节点数目 = 删除后总节点数目”是否成立
        System.out.println(" (out1.size() - out2.size()) == delList.size()? " + ((middleOderOutput1.size() - middleOderOutput2.size()) == delList.size()));
        // 测试rbt中“删除前节点  包含全部  删除后节点”是否成立
        System.out.println("out1.containsAll(out2)? " + (middleOderOutput1.containsAll(middleOderOutput2)));
        // 测试rbt中“删除前节点  包含全部  被删除节点”是否成立
        System.out.println("out1.containsAll(delList)? " + (middleOderOutput1.containsAll(delList)));
    }
}

RbTree.java

package rbt;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;


public class RbTree<T extends Comparable<T>> {

    /**
     * 根结点
     */

    private RbTreeNode<T> mRoot;
    private static final boolean RED = false;
    private static final boolean BLACK = true;

    public RbTree() {
        mRoot = null;
    }

    /**
     * 获取某个节点的父节点
     *
     * @param node
     * @return
     */

    private RbTreeNode<T> parentOf(RbTreeNode<T> node) {
        return node != null ? node.parent : null;
    }

    /**
     * 获取某个节点的颜色
     *
     * @param node
     * @return
     */

    private boolean colorOf(RbTreeNode<T> node) {
        return node != null ? node.color : BLACK;
    }

    /**
     * 判断某个节点是否为红色
     *
     * @param node
     * @return
     */

    private boolean isRed(RbTreeNode<T> node) {
        return ((node != null) && (node.color == RED)) ? true : false;
    }

    /**
     * 判断某个节点是否为黑色
     *
     * @param node
     * @return
     */

    private boolean isBlack(RbTreeNode<T> node) {
        return !isRed(node);
    }

    /**
     * 设置某个节点为黑色
     *
     * @param node
     */

    private void setBlack(RbTreeNode<T> node) {
        if (node != null) {
            node.color = BLACK;
        }
    }

    /**
     * 设置某个节点为红色
     *
     * @param node
     */

    private void setRed(RbTreeNode<T> node) {
        if (node != null) {
            node.color = RED;
        }
    }

    /**
     * 设置某个节点的父节点
     *
     * @param node
     * @param parent
     */

    private void setParent(RbTreeNode<T> node, RbTreeNode<T> parent) {
        if (node != null) {
            node.parent = parent;
        }
    }

    /**
     * 设置某个节点的颜色
     *
     * @param node
     * @param color
     */

    private void setColor(RbTreeNode<T> node, boolean color) {
        if (node != null) {
            node.color = color;
        }
    }

    public void preOrder() {
        preOrder(mRoot);
    }

    /**
     * 前序遍历红黑树
     *
     * @param tree
     */

    private void preOrder(RbTreeNode<T> tree) {
        if (tree != null) {
            System.out.print(tree.nodeKey + "->");
            preOrder(tree.left);
            preOrder(tree.right);
        }
    }


    /**
     * 中序遍历红黑树
     *
     * @param tree
     * @param collection 用于记录遍历的过程
     */

    private void inOrder(RbTreeNode<T> tree, Collection<T> collection) {
        if (tree != null) {
            inOrder(tree.left, collection);
            //System.out.print(tree.nodeKey + "->");
            collection.add(tree.nodeKey);

            // 用于验证节点是否符合“如果一个节点是红色的,则它的子节点必须是黑色的”
            if (tree.color == RED) {
                if (tree.left != null && tree.left.color == RED) {
                    System.out.print(" 该节点违反红黑树特性!!! ");
                }
                if (tree.right != null && tree.right.color == RED) {
                    System.out.print(" 该节点违反红黑树特性!!! ");
                }
            }

            inOrder(tree.right, collection);
        }
    }

    public void inOrder(Collection<T> collection) {
        inOrder(mRoot, collection);
    }

    /**
     * 后序遍历红黑树
     *
     * @param tree
     */

    private void postOrder(RbTreeNode<T> tree) {
        if (tree != null) {
            postOrder(tree.left);
            postOrder(tree.right);
            System.out.print(tree.nodeKey + "->");
        }
    }


    public void printAllRootToLeafPaths(List<RbTreeNode<T>> path) {
        printAllRootToLeafPaths(mRoot, path);
    }

    /**
     * 打印节点root到每个叶子节点(Nil)的路径上的黑节点个数
     *
     * @param root
     * @param path
     */

    public void printAllRootToLeafPaths(RbTreeNode<T> root, List<RbTreeNode<T>> path) {
        if (root == null) {
            return;
        }
        path.add(root);

        if (root.left == null && root.right == null) {
            path = path.stream().filter(e -> e.color).collect(Collectors.toList());
            //System.out.println("当前路径的黑节点数量:" + path.size());
            System.out.print("->" + path.size());
            return;
        } else {
            printAllRootToLeafPaths(root.left, new LinkedList<>(path));
            printAllRootToLeafPaths(root.right, new LinkedList<>(path));
        }
    }

    public void printTree() {
        printTree(mRoot);
    }

    /**
     * 打印红黑树每一层的节点,格式为:
     * 当前节点键值(当前节点的颜色  父节点键值  是父节点的左孩子还是右孩子)
     * 例如:1905(B 1971 L)
     *
     * @param root
     */

    public void printTree(RbTreeNode<T> root) {
        java.util.LinkedList<RbTreeNode<T>> queue = new java.util.LinkedList<RbTreeNode<T>>();
        java.util.LinkedList<RbTreeNode<T>> queue2 = new java.util.LinkedList<RbTreeNode<T>>();
        if (root == null) {
            return;
        }
        queue.add(root);
        boolean firstQueue = true;

        while (!queue.isEmpty() || !queue2.isEmpty()) {
            java.util.LinkedList<RbTreeNode<T>> q = firstQueue ? queue : queue2;
            RbTreeNode<T> n = q.poll();

            if (n != null) {
                String pos = n.parent == null ? "" : (n == n.parent.left
                        ? " L" : " R");
                String pstr = n.parent == null ? "" : n.parent.toString();
                String cstr = n.color ? "B" : "R";
                cstr = n.parent == null ? cstr : cstr + " ";
                System.out.print(n + "(" + (cstr) + pstr + (pos) + ")" + " ");
                if (n.left != null) {
                    (firstQueue ? queue2 : queue).add(n.left);
                }
                if (n.right != null) {
                    (firstQueue ? queue2 : queue).add(n.right);
                }
            } else {
                System.out.println();
                firstQueue = !firstQueue;
            }
        }
    }


    /**
     * (递归实现)查找红黑树中键值为nodeKey的节点
     *
     * @param root
     * @param nodeKey
     * @return
     */

    private RbTreeNode<T> searchRecursively(RbTreeNode<T> root, T nodeKey) {
        if (root == null) {
            return root;
        }

        int cmp = nodeKey.compareTo(root.nodeKey);
        if (cmp < 0) {
            return searchRecursively(root.left, nodeKey);
        } else if (cmp > 0) {
            return searchRecursively(root.right, nodeKey);
        } else {
            return root;
        }
    }


    /**
     * 左旋:以某个结点P作为支点(旋转结点),其右子结点V变为
     * 旋转结点P的父结点,右子结点V的左子结点R变为旋转结点
     * P的右子结点,左子结点F保持不变。
     *
     * @param pNode 支点(旋转结点)
     */

    private void leftRotate(RbTreeNode<T> pNode) {
        // P的右子结点V的左子结点R变为旋转结点P的右子结点
        RbTreeNode<T> vNode = pNode.right;
        RbTreeNode<T> rNode = vNode.left;
        pNode.right = rNode;

        // 修正R的parent为P
        if (rNode != null) {
            rNode.parent = pNode;
        }

        // 修正V的parent为P原来的parent
        vNode.parent = pNode.parent;

        if (pNode.parent == null) {
            // 如果P原来就没有parent,说明P原来就是根节点。现
            // 在V要变成P的parent,则新的根节点要更新为V
            this.mRoot = vNode;
        } else {
            // 如果P原来就有parent,则V取代P作为这个parent的左
            // 孩子或右孩子
            if (pNode.parent.left == pNode) {
                pNode.parent.left = vNode;
            } else {
                pNode.parent.right = vNode;
            }
        }

        // 旋转结点P变为结点V的左孩子
        vNode.left = pNode;
        // 结点V变为旋转结点P的父结点
        pNode.parent = vNode;
    }

    /**
     * 右旋:以某个结点P作为支点(旋转结点),其左子结点F变为
     * 旋转结点P的父结点,左子结点F的右子结点K变为旋转结点
     * P的左子结点,右子结点V保持不变。
     *
     * @param pNode
     */

    private void rightRotate(RbTreeNode<T> pNode) {
        // P的左子结点F的右子结点K变为旋转结点P的左子结点
        RbTreeNode<T> fNode = pNode.left;
        RbTreeNode<T> kNode = fNode.right;
        pNode.left = kNode;

        // 修正K的parent为P
        if (kNode != null) {
            kNode.parent = pNode;
        }

        // 修正F的parent为P原来的parent
        fNode.parent = pNode.parent;

        if (pNode.parent == null) {
            // 如果P原来就没有parent,说明P原来就是根节点。现
            // 在F要变成P的parent,则新的根节点要更新为F
            this.mRoot = fNode;
        } else {
            // 如果P原来就有parent,则F取代P作为这个parent的左
            // 孩子或右孩子
            if (pNode == pNode.parent.right) {
                pNode.parent.right = fNode;
            } else {
                pNode.parent.left = fNode;
            }
        }

        // 旋转结点P变为结点F的右孩子
        fNode.right = pNode;
        // 结点F变为旋转结点P的父结点
        pNode.parent = fNode;
    }

    /**
     * 红黑树插入修正函数
     *
     * @param currentNode
     */

    private void insertFixUp(RbTreeNode<T> currentNode) {
        RbTreeNode<T> parent;
        RbTreeNode<T> gparent;

        // INSERT-FIXUP-CASE-3:当前节点的父节点存在,并且父节点的颜色是红色
        while (((parent = parentOf(currentNode)) != null) && isRed(parent)) {
            gparent = parentOf(parent);
            // 若当前节点的父节点是祖父节点的左孩子
            if (parent == gparent.left) {
                // INSERT-FIXUP-CASE-3.1:当前节点的叔叔节点是红色
                RbTreeNode<T> uncle = gparent.right;
                if ((uncle != null) && isRed(uncle)) {
                    setBlack(uncle);
                    setBlack(parent);
                    setRed(gparent);
                    currentNode = gparent;
                    continue;
                }
                // INSERT-FIXUP-CASE-3.2:当前节点的叔叔节点是黑色,且当前节点是右孩子
                if (parent.right == currentNode) {
                    RbTreeNode<T> tmp;
                    leftRotate(parent);
                    tmp = parent;
                    parent = currentNode;
                    currentNode = tmp;
                }
                // INSERT-FIXUP-CASE-3.3:当前节点的叔叔节点是黑色,且当前节点是左孩子
                setBlack(parent);
                setRed(gparent);
                rightRotate(gparent);
            }
            // 若当前节点的父节点是祖父节点的右孩子
            else {
                // INSERT-FIXUP-CASE-3.1:当前节点的叔叔节点是红色
                RbTreeNode<T> uncle = gparent.left;
                if ((uncle != null) && isRed(uncle)) {
                    setBlack(uncle);
                    setBlack(parent);
                    setRed(gparent);
                    currentNode = gparent;
                    continue;
                }
                // INSERT-FIXUP-CASE-3.2:当前节点的叔叔节点是黑色,且当前节点是左孩子
                if (parent.left == currentNode) {
                    RbTreeNode<T> tmp;
                    rightRotate(parent);
                    tmp = parent;
                    parent = currentNode;
                    currentNode = tmp;
                }
                // INSERT-FIXUP-CASE-3.3:当前节点的叔叔节点是黑色,且当前节点是右孩子
                setBlack(parent);
                setRed(gparent);
                leftRotate(gparent);
            }
        }

        // INSERT-FIXUP-CASE-1:被插入的节点是根节点,将根节点设为黑色
        if (currentNode == mRoot) {
            setBlack(this.mRoot);
            return;
        }
        // INSERT-FIXUP-CASE-2:被插入的节点的父节点是黑色,什么也不需要做
        if (parentOf(currentNode) != null && isBlack(parentOf(currentNode))) {
            // DO-NOTHONG
            return;
        }
    }

    /**
     * (递归实现)将结点插入到红黑树中
     *
     * @param root
     * @param nodeInserted
     * @return
     */

    public boolean insertRecursively(RbTreeNode<T> root,
                                     RbTreeNode<T> nodeInserted)
 
{
        if (root == null) {
            mRoot = nodeInserted;
            return true;
        }

        if (root.nodeKey.compareTo(nodeInserted.nodeKey) > 0) {
            if (root.left == null) {
                // 1. 将红黑树当作一颗二叉查找树,将节点添加到二叉查找树中
                root.left = nodeInserted;
                nodeInserted.parent = root;
                // 2. 设置节点的颜色为红色
                nodeInserted.color = RED;
                // 3. 通过旋转和变色将它重新修正为一颗二叉查找树
                insertFixUp(nodeInserted);
                return true;
            } else {
                // 向左子树中递归操作
                return insertRecursively(root.left, nodeInserted);
            }
        } else if (root.nodeKey.compareTo(nodeInserted.nodeKey) < 0) {
            if (root.right == null) {
                // 1. 将红黑树当作一颗二叉查找树,将节点添加到二叉查找树中
                root.right = nodeInserted;
                nodeInserted.parent = root;
                // 2. 设置节点的颜色为红色
                nodeInserted.color = RED;
                // 3. 通过旋转和变色将它重新修正为一颗二叉查找树
                insertFixUp(nodeInserted);
                return true;
            } else {
                // 向右子树中递归操作
                return insertRecursively(root.right, nodeInserted);
            }
        } else {
            return false;
        }
    }

    /*
     * 新建结点(nodeKey),并将其插入到红黑树中
     *
     * 参数说明:
     *     nodeKey 插入结点的键值
     */

    public void insert(T nodeKey) {
        RbTreeNode<T> node = new RbTreeNode<T>(nodeKey, BLACK, nullnullnull);

        // 如果新建结点失败,则返回
        if (node != null) {
            insertRecursively(mRoot, node);
        }
    }


    /**
     * 红黑树删除修正函数
     * @param childOfNodeRemoved
     * @param parentOfNodeRemoved
     */

    private void removeFixUp(RbTreeNode<T> childOfNodeRemoved,
                             RbTreeNode<T> parentOfNodeRemoved)
 
{
        // other是childOfNodeRemoved的兄弟节点
        RbTreeNode<T> other;

        // REMOVE-FIXUP-CASE-3: childOfNodeRemoved是“黑+黑”节点,
        // 且childOfNodeRemoved不是根
        while ((childOfNodeRemoved == null || isBlack(childOfNodeRemoved))
                && (childOfNodeRemoved != this.mRoot)) {
            if (parentOfNodeRemoved.left == childOfNodeRemoved) {
                other = parentOfNodeRemoved.right;
                if (isRed(other)) {
                    // REMOVE-FIXUP-CASE-3.1: childOfNodeRemoved的兄弟
                    // other是红色
                    setBlack(other);
                    setRed(parentOfNodeRemoved);
                    leftRotate(parentOfNodeRemoved);
                    other = parentOfNodeRemoved.right;
                }
                if ((other.left == null || isBlack(other.left)) &&
                        (other.right == null || isBlack(other.right))) {
                    // REMOVE-FIXUP-CASE-3.2: childOfNodeRemoved的兄弟
                    // other是黑色,且other的两个孩子也都是黑色
                    setRed(other);
                    childOfNodeRemoved = parentOfNodeRemoved;
                    parentOfNodeRemoved = parentOf(childOfNodeRemoved);
                } else {
                    if (other.right == null || isBlack(other.right)) {
                        // REMOVE-FIXUP-CASE-3.3: childOfNodeRemoved的兄弟
                        // other是黑色,并且other的左孩子是红色,右孩子是黑色
                        setBlack(other.left);
                        setRed(other);
                        rightRotate(other);
                        other = parentOfNodeRemoved.right;
                    }
                    // REMOVE-FIXUP-CASE-3.4: childOfNodeRemoved的兄弟other
                    // 是黑色,并且other的右孩子是红色,左孩子是任意颜色
                    setColor(other, colorOf(parentOfNodeRemoved));
                    setBlack(parentOfNodeRemoved);
                    setBlack(other.right);
                    leftRotate(parentOfNodeRemoved);
                    childOfNodeRemoved = this.mRoot;
                    break;
                }
            } else {
                other = parentOfNodeRemoved.left;
                if (isRed(other)) {
                    // REMOVE-FIXUP-CASE-3.1: childOfNodeRemoved的兄弟
                    // other是红色
                    setBlack(other);
                    setRed(parentOfNodeRemoved);
                    rightRotate(parentOfNodeRemoved);
                    other = parentOfNodeRemoved.left;
                }
                if ((other.left == null || isBlack(other.left)) &&
                        (other.right == null || isBlack(other.right))) {
                    // REMOVE-FIXUP-CASE-3.2: childOfNodeRemoved的兄弟
                    // other是黑色,且other的两个孩子也都是黑色
                    setRed(other);
                    childOfNodeRemoved = parentOfNodeRemoved;
                    parentOfNodeRemoved = parentOf(childOfNodeRemoved);
                } else {
                    if (other.left == null || isBlack(other.left)) {
                        // REMOVE-FIXUP-CASE-3.3: childOfNodeRemoved的兄弟
                        // other是黑色,并且other的左孩子是红色,右孩子是黑色
                        setBlack(other.right);
                        setRed(other);
                        leftRotate(other);
                        other = parentOfNodeRemoved.left;
                    }
                    // REMOVE-FIXUP-CASE-3.4: childOfNodeRemoved的兄弟other
                    // 是黑色,并且other的右孩子是红色,左孩子是任意颜色
                    setColor(other, colorOf(parentOfNodeRemoved));
                    setBlack(parentOfNodeRemoved);
                    setBlack(other.left);
                    rightRotate(parentOfNodeRemoved);
                    childOfNodeRemoved = this.mRoot;
                    break;
                }
            }
        }

        //  REMOVE-FIXUP-CASE-1:childOfNodeRemoved是“红+黑”节点 ,
        // 直接把childOfNodeRemoved设为黑色
        if (childOfNodeRemoved != null && isRed(childOfNodeRemoved)) {
            setBlack(childOfNodeRemoved);
            return;
        }
        // REMOVE-FIXUP-CASE-2:childOfNodeRemoved是“黑+黑”节点,
        // 且childOfNodeRemoved是根,什么都不做
        if (childOfNodeRemoved != null && isBlack(childOfNodeRemoved)
                && childOfNodeRemoved == this.mRoot) {
            // DO-NOTHING
            return;
        }
    }

    /**
     * 删除结点
     *
     * @param nodeRemoved
     */

    private void remove(RbTreeNode<T> nodeRemoved) {
        // 被删除节点的左右孩子都不为空的情况
        if ((nodeRemoved.left != null) && (nodeRemoved.right != null)) {
            removeNodeWithDoubleChild(nodeRemoved);
        }
        // 被删除节点的左孩子不为空的情况
        else if (nodeRemoved.left != null) {
            removeNodeWithOnlyLeftChild(nodeRemoved);
        }
        // 被删除节点的右孩子不为空的情况
        else if (nodeRemoved.right != null) {
            removeNodeWithOnlyRightChild(nodeRemoved);
        }
        // 被删除节点的左右孩子都为空的情况
        else {
            removeNodeWithNoChild(nodeRemoved);
        }
    }

    /**
     * 被刪除节点的左右子树均不存在,直接删掉该节点即可
     *
     * @param nodeRemoved
     */

    private void removeNodeWithNoChild(RbTreeNode<T> nodeRemoved) {
        RbTreeNode<T> childOfNodeRemoved;
        RbTreeNode<T> parenOfNodeRemoved;
        boolean colorOfNodeRemoved;

        childOfNodeRemoved = null;
        parenOfNodeRemoved = nodeRemoved.parent;
        colorOfNodeRemoved = nodeRemoved.color;

        // 被删除节点不是根节点(根节点不存在父节点)
        if (parentOf(nodeRemoved) != null) {
            if (parentOf(nodeRemoved).left == nodeRemoved) {
                parentOf(nodeRemoved).left = childOfNodeRemoved;
            } else {
                parentOf(nodeRemoved).right = childOfNodeRemoved;
            }
        } else {
            // 被删除节点是根节点,更新根节点
            this.mRoot = childOfNodeRemoved;
        }

        // 刪除修正
        if (colorOfNodeRemoved == BLACK) {
            removeFixUp(childOfNodeRemoved, parenOfNodeRemoved);
        }

        return;
    }

    /**
     * 被删除节点只有右子树,用被删除节点的右子树替换被删除节点即可
     *
     * @param nodeRemoved
     */

    private void removeNodeWithOnlyRightChild(RbTreeNode<T> nodeRemoved) {
        RbTreeNode<T> childOfNodeRemoved;
        RbTreeNode<T> parenOfNodeRemoved;
        boolean colorOfNodeRemoved;

        childOfNodeRemoved = nodeRemoved.right;
        parenOfNodeRemoved = nodeRemoved.parent;
        colorOfNodeRemoved = nodeRemoved.color;

        childOfNodeRemoved.parent = parenOfNodeRemoved;

        // 被删除节点不是根节点(根节点不存在父节点)
        if (parentOf(nodeRemoved) != null) {
            if (parentOf(nodeRemoved).left == nodeRemoved) {
                parentOf(nodeRemoved).left = childOfNodeRemoved;
            } else {
                parentOf(nodeRemoved).right = childOfNodeRemoved;
            }
        } else {
            // 被删除节点是根节点,更新根节点
            this.mRoot = childOfNodeRemoved;
        }

        // 刪除修正
        if (colorOfNodeRemoved == BLACK) {
            removeFixUp(childOfNodeRemoved, parenOfNodeRemoved);
        }

        return;
    }

    /**
     * 被删除节点只有左子树,用被删除节点的左子树替换被删除节点即可
     *
     * @param nodeRemoved
     */

    private void removeNodeWithOnlyLeftChild(RbTreeNode<T> nodeRemoved) {
        RbTreeNode<T> childOfNodeRemoved;
        RbTreeNode<T> parenOfNodeRemoved;
        boolean colorOfNodeRemoved;

        childOfNodeRemoved = nodeRemoved.left;
        parenOfNodeRemoved = nodeRemoved.parent;
        colorOfNodeRemoved = nodeRemoved.color;

        childOfNodeRemoved.parent = parenOfNodeRemoved;

        // 被删除节点不是根节点(根节点不存在父节点)
        if (parentOf(nodeRemoved) != null) {
            if (parentOf(nodeRemoved).left == nodeRemoved) {
                parentOf(nodeRemoved).left = childOfNodeRemoved;
            } else {
                parentOf(nodeRemoved).right = childOfNodeRemoved;
            }
        } else {
            // 被删除节点是根节点,更新根节点
            this.mRoot = childOfNodeRemoved;
        }

        // 刪除修正
        if (colorOfNodeRemoved == BLACK) {
            removeFixUp(childOfNodeRemoved, parenOfNodeRemoved);
        }

        return;
    }

    /**
     * 被删除节点的左右孩子都不为空的情况,用被删除节点的直接前驱
     * (或直接后继)来代替被删除节点,同时在二叉排序树中对其直接
     * 前驱(或直接后继)做删除操作。这里针对后继节点进行操作。
     *
     * @param nodeRemoved
     */

    private void removeNodeWithDoubleChild(RbTreeNode<T> nodeRemoved) {
        // 找到当前节点的的中序后继节点,称为取代节点
        RbTreeNode<T> replace = nodeRemoved.right;
        while (replace.left != null) {
            replace = replace.left;
        }

        // 拷贝替代节点的内容到被删除节点(这里只拷贝nodeKey,实际上一个
        // 节点除了有nodeKey域,还有nodeData域,只不过我们简化了节点定
        // 义,拷贝过程应该连同nodeData一起拷贝)
        nodeRemoved.nodeKey = replace.nodeKey;
        // 然后删除替代节点
        remove(replace);
        return;
    }

    /**
     * 删除键值为nodeKey的结点
     *
     * @param nodeKey
     */

    public void remove(T nodeKey) {
        RbTreeNode<T> node;

        if ((node = searchRecursively(mRoot, nodeKey)) != null) {
            remove(node);
        }
    }

}


class RbTreeNode<T extends Comparable<T>> {
    /**
     * 颜色,黑-true / 红-false
     */

    boolean color;
    /**
     * 关键字(键值)
     */

    T nodeKey;
    /**
     * 左孩子
     */

    RbTreeNode<T> left;
    /**
     * 右孩子
     */

    RbTreeNode<T> right;
    /**
     * 父结点
     */

    RbTreeNode<T> parent;

    public RbTreeNode() {
    }

    public RbTreeNode(
            T nodeKey, boolean color, RbTreeNode<T> parent,
            RbTreeNode<T> left, RbTreeNode<T> right)
 
{
        this.nodeKey = nodeKey;
        this.color = color;
        this.parent = parent;
        this.left = left;
        this.right = right;
    }

    @Override
    public String toString() {
        return nodeKey.toString();
    }
}
原文地址:https://www.cnblogs.com/xiekun/p/14386910.html