【树】——从二叉树到AVL树再到红黑树

一、前言


代码自然会存在很多潜在的bug或者不精炼之处,望好心人指教,教主当不胜感激

这篇博客的目的

二叉树本身是以递归的方式定义的,而现有的大部分二叉树的代码的都是以 树的节点作为二叉树的内部类的方式设计的。

虽然这样的设计的确更容易实现继承,写出更简洁的代码。然而本教主向来对递归比较头疼,所以希望能简单的重构一下二叉树的实现。

代码组织

所以教主希望在接下来代码的组织中

  1. 最简单的二叉树就用递归的方法来构造
  2. 先将一些递归算法改写成非递归算法
  3. 然后再添加一些旋转算法改造成AVL树
  4. 至于红黑树,本教主觉得其实有点难受的,需要考虑的情况和算法的细节都特别多,所以还是以TreeMap的实现为例,构造一棵具有基本功能的最简单的红黑树

引发的问题

  1. 用递归的方式定义,意味着类中基本都是些静态方法,也就是说,继承和接口在这样的设计中将无法体现用处。

  2. 由于红黑树各种情况太多,难以找到一个能测试所以情况的一组数据来把图粘上来,但是。数据结构可视化工具 (一个基于canvas的轻小工具)可以用上一用,另外呢数据取自后面的test第二组数据)在这里插入图片描述


目录

二叉树

(1)二叉树定义

(2)二叉树遍历的非递归算法

(3)二叉树高度计算算法

(4)二叉树根据先根遍历创建二叉树算法 (这个算法在大部分教科书上都有,本教主只是稍微让这个算法更加友好,易调用)

(5)二叉树完整代码

AVL树

(1)AVL树定义

(2)AVL树的树根实例存储算法(递归定义导致不再有外部类来提供root字段去保存一个树集的树根实例,那就用树根池来存)

(3)AVL树平衡因子计算算法

(4)AVL树四种旋转算法

(5)AVL树插入算法

(6)AVL树完整代码

(7)AVL树 --> test

红黑树

(1)红黑树的性质

(2)红黑树的定义

(3)红黑树的getter和Setter(参照TreeMap源码,设计红黑树的人巧妙的将处理空指针的逻辑封装在了这些getter和setter中)

(4)红黑树的旋转算法

(5)红黑树的 put 及其调整算法

(6)红黑树的 remove 及其调整算法

(7)红黑树的mini版完整代码

(8)红黑树 --> test





二、二叉树

二叉树定义

public class BinaryTree<T> {
    private T             data;
    private BinaryTree<T> leftChild;
    private BinaryTree<T> rightChild;
}

二叉树遍历的非递归算法

	public static <T> List<T> preOrderTraverse(BinaryTree<T> tree) {
        List<T>              resultList = new ArrayList<>();
        Stack<BinaryTree<T>> stack      = new Stack<>();
        BinaryTree<T>        t          = tree;
        while (null != t || !stack.isEmpty()) {
            if (null != t) {
                resultList.add(t.data);
                stack.push(t);
                t = t.leftChild;
            } else {
                t = stack.pop();
                t = t.rightChild;
            }
        }
        return resultList;
    }
	public static <T> List<T> inOrderTraverse(BinaryTree<T> tree) {
        List<T>              resultList = new ArrayList<>();
        Stack<BinaryTree<T>> stack      = new Stack<>();
        BinaryTree<T>        t          = tree;
        while (null != t || !stack.isEmpty()) {
            if (null != t) {
                stack.push(t);
                t = t.leftChild;
            } else {
                t = stack.pop();
                resultList.add(t.data);
                t = t.rightChild;
            }
        }
        return resultList;
    }
	public static <T> List<T> postOrderTraverse(BinaryTree<T> tree) {
        List<T>              resultList = new ArrayList<>();
        Stack<BinaryTree<T>> stack      = new Stack<>();
        BinaryTree<T>        t          = tree;
        BinaryTree<T>        last       = tree;
        while (null != t || !stack.isEmpty()) {
            if (null != t) {
                stack.push(t);
                t = t.leftChild;
            } else {
                //不移除队头
                t = stack.peek();
                if (null == t.rightChild || last == t.rightChild) {
                    resultList.add(t.data);
                    stack.pop();
                    last = t;
                    t    = null;
                } else {
                    t = t.rightChild;
                }
            }
        }
        return resultList;
    }
	public static <T> List<T> levelOrderTraverse(BinaryTree<T> tree) {
        List<T>              resultList = new ArrayList<>();
        Queue<BinaryTree<T>> queue      = new LinkedList<>();
        BinaryTree<T>        t          = tree;
        queue.offer(t);
        while (!queue.isEmpty()) {
            t = queue.poll();
            resultList.add(t.data);
            if (null != t.leftChild) {
                queue.offer(t.leftChild);
            }
            if (null != t.rightChild) {
                queue.offer(t.rightChild);
            }
        }
        return resultList;
    }

二叉树高度计算算法

	public static<T> int getHeight(BinaryTree<T> tree) {
        if (null == tree) {
            return 0;
        } else {
            int leftHeight  = getHeight(tree.leftChild);
            int rightHeight = getHeight(tree.rightChild);
            return Math.max(leftHeight, rightHeight) + 1;
        }
    }

二叉树根据先序序列创建二叉树的算法(null占位)

	public static <T> List<T> inOrderTraverse(BinaryTree<T> tree) {
        List<T>              resultList = new ArrayList<>();
        Stack<BinaryTree<T>> stack      = new Stack<>();
        BinaryTree<T>        t          = tree;
        while (null != t || !stack.isEmpty()) {
            if (null != t) {
                stack.push(t);
                t = t.leftChild;
            } else {
                t = stack.pop();
                resultList.add(t.data);
                t = t.rightChild;
            }
        }
        return resultList;
    }

二叉树完整代码

package xyz.mstar.binary;
import com.sun.istack.internal.NotNull;
import java.util.*;

/**
 * @author IceFery
 */
public class BinaryTree<T> {
    private T             data;
    private BinaryTree<T> leftChild;
    private BinaryTree<T> rightChild;

    /**
     * 以先序遍历的方式创建一颗二叉树
     * @param dataList
     * @param <T>
     * @return 一颗二叉树
     */
    public static <T> BinaryTree<T> createTreeByPreOrder(@NotNull List<T> dataList) {
        T element = dataList.get(0);
        dataList.remove(0);
        if (!dataList.isEmpty() && null != element) {
            BinaryTree<T> tree = new BinaryTree<>();
            tree.data       = element;
            tree.leftChild  = BinaryTree.createTreeByPreOrder(dataList);
            tree.rightChild = BinaryTree.createTreeByPreOrder(dataList);
            return tree;
        } else {
            return null;
        }
    }

    /**
     * 获取该而二叉树的高度
     * @param tree
     * @return tree.height
     */
    public static <T> int getHeight(BinaryTree<T> tree) {
        if (null == tree) {
            return 0;
        } else {
            int leftHeight  = getHeight(tree.leftChild);
            int rightHeight = getHeight(tree.rightChild);
            return Math.max(leftHeight, rightHeight) + 1;
        }
    }

    /**
     * 先序一颗遍历二叉树,并返回遍历的序列
     * @param tree
     * @param <T>
     * @return traverseList
     */
    public static <T> List<T> preOrderTraverse(@NotNull BinaryTree<T> tree) {
        List<T>              resultList = new ArrayList<>();
        Stack<BinaryTree<T>> stack      = new Stack<>();
        BinaryTree<T>        t          = tree;
        while (null != t || !stack.isEmpty()) {
            if (null != t) {
                resultList.add(t.data);
                stack.push(t);
                t = t.leftChild;
            } else {
                t = stack.pop();
                t = t.rightChild;
            }
        }
        return resultList;
    }

    /**
     * 中序遍历一颗二叉树,并返回遍历序列
     * @param tree
     * @param <T>
     * @return traverseList
     */
    public static <T> List<T> inOrderTraverse(@NotNull BinaryTree<T> tree) {
        List<T>              resultList = new ArrayList<>();
        Stack<BinaryTree<T>> stack      = new Stack<>();
        BinaryTree<T>        t          = tree;
        while (null != t || !stack.isEmpty()) {
            if (null != t) {
                stack.push(t);
                t = t.leftChild;
            } else {
                t = stack.pop();
                resultList.add(t.data);
                t = t.rightChild;
            }
        }
        return resultList;
    }

    /**
     * 后序遍历一颗二叉树,并返回遍历序列
     * @param tree
     * @param <T>
     * @return traverseList
     */
    public static <T> List<T> postOrderTraverse(@NotNull BinaryTree<T> tree) {
        List<T>              resultList = new ArrayList<>();
        Stack<BinaryTree<T>> stack      = new Stack<>();
        BinaryTree<T>        t          = tree;
        BinaryTree<T>        last       = tree;
        while (null != t || !stack.isEmpty()) {
            if (null != t) {
                stack.push(t);
                t = t.leftChild;
            } else {
                //不移除队头
                t = stack.peek();
                if (null == t.rightChild || last == t.rightChild) {
                    resultList.add(t.data);
                    stack.pop();
                    last = t;
                    t    = null;
                } else {
                    t = t.rightChild;
                }
            }
        }
        return resultList;
    }

    /**
     * 层次遍历一颗二叉树,并返回遍历序列
     * @param tree
     * @param <T>
     * @return traverseList
     * @throws Exception
     */
    public static <T> List<T> levelOrderTraverse(@NotNull BinaryTree<T> tree) {
        List<T>              resultList = new ArrayList<>();
        Queue<BinaryTree<T>> queue      = new LinkedList<>();
        BinaryTree<T>        t          = tree;
        queue.offer(t);
        while (!queue.isEmpty()) {
            t = queue.poll();
            resultList.add(t.data);
            if (null != t.leftChild) {
                queue.offer(t.leftChild);
            }
            if (null != t.rightChild) {
                queue.offer(t.rightChild);
            }
        }
        return resultList;
    }
}


三、AVL树

AVL树定义

public class AVLTree<T extends Comparable> {
    /**
     * 将创建的树集的树根引用保存树根池中
     */
    private static Map<String, AVLTree> instancePool = new HashMap<>();

    private T          data;
    private AVLTree<T> leftChild;
    private AVLTree<T> rightChild;

    private AVLTree(T data) {
        this.data = data;
    }
}

AVL树的树根实例存储算法

    public static <T extends Comparable> AVLTree<T> getInstance(String treeName) {
        return (instancePool.isEmpty() || !instancePool.containsKey(treeName)) ? null : (AVLTree<T>) instancePool.get(treeName);
    }

AVL树平衡因子计算算法

    private static <T extends Comparable> boolean isBalanced(AVLTree<T> tree) {
        return Math.abs(getHeight(tree.leftChild) - getHeight(tree.rightChild)) < 2;
    }

AVL树四种旋转算法

在这里插入图片描述
    private static <T extends Comparable> AVLTree<T> LL(AVLTree<T> unbalancedTree) {
        //不平衡树的左节点成为新平衡树
        AVLTree<T> balancedTree = unbalancedTree.leftChild;
        //原【新平衡树】的右子树成为原【不平衡树】的左子树
        unbalancedTree.leftChild = balancedTree.rightChild;
        //原【不平衡树】成为【新平衡树】的右子树
        balancedTree.rightChild = unbalancedTree;
        return balancedTree;
    }

在这里插入图片描述

    private static <T extends Comparable> AVLTree<T> RR(AVLTree<T> unbalancedTree) {
        //不平衡树的右节点成为新平衡树
        AVLTree<T> balancedTree = unbalancedTree.rightChild;
        //原【新平衡树】的左子树成为原【不平衡树】的右子树
        unbalancedTree.rightChild = balancedTree.leftChild;
        //原【不平衡树】成为【新平衡树】的左子树
        balancedTree.leftChild = unbalancedTree;
        return balancedTree;
    }

在这里插入图片描述

    private static <T extends Comparable> AVLTree<T> LR(AVLTree<T> unbalancedTree) {
        //先对不平衡树的左子树进行单次左旋),转化为【左-左型】
        unbalancedTree.leftChild = RR(unbalancedTree.leftChild);
        //再对不平衡树进行单次右旋
        return LL(unbalancedTree);
    }

在这里插入图片描述

    private static <T extends Comparable> AVLTree<T> RL(AVLTree<T> unbalancedTree) {
        //先对不平衡树的右子树进行单次右旋,转化为【右-右型】
        unbalancedTree.rightChild = LL(unbalancedTree.rightChild);
        //再对不平衡树进行单次单次左旋
        return RR(unbalancedTree);
    }

AVL树插入算法

    public static <T extends Comparable> AVLTree<T> insert(String treeName, @NotNull T data) {
        if (null == getInstance(treeName)) {
            AVLTree<T> tree = new AVLTree<>(data);
            instancePool.put(treeName, tree);
            return tree;
        } else {
            return insert(getInstance(treeName), treeName, data);
        }
    }

    private static <T extends Comparable> AVLTree<T> insert(AVLTree<T> tree, String treeName, @NotNull T data) {
        if (null == tree) {
            tree = new AVLTree<>(data);
        } else if (data.compareTo(tree.data) < 0) {
            //向左子树寻找插入位置
            tree.leftChild = insert(tree.leftChild, treeName, data);
            //维持平衡
            if (!isBalanced(tree)) {
                AVLTree<T> temp = tree;
                if (data.compareTo(tree.leftChild.data) < 0) {
                    //如果新树的值比不平衡树的左孩子值小,则该不平衡树为LL型
                    tree = LL(tree);
                } else if (data.compareTo(tree.leftChild.data) > 0) {
                    //如果新树的值比不平衡树的左孩子值大,则该不平衡树为LR型
                    tree = LR(tree);
                }
                //如果旋转导致了树根的变化,那么需要更新树根池中的引用
                if (temp == getInstance(treeName)) {
                    instancePool.put(treeName, tree);
                }
            }
        } else if (data.compareTo(tree.data) > 0) {
            //向右子树寻找插入位置
            tree.rightChild = insert(tree.rightChild, treeName, data);
            if (!isBalanced(tree)) {
                AVLTree<T> temp = tree;
                if (data.compareTo(tree.rightChild.data) < 0) {
                    tree = RL(tree);
                } else if (data.compareTo(tree.rightChild.data) > 0) {
                    tree = RR(tree);
                }
                if (temp == getInstance(treeName)) {
                    instancePool.put(treeName, tree);
                }
            }
        }
        return tree;
    }

AVL树完整代码

package xyz.mstar.tree;
import com.sun.istack.internal.NotNull;
import java.util.*;

/**
 * @author IceFery
 */
public class AVLTree<T extends Comparable> {
    /**
     * 将创建的树集的树根引用保存树根池中
     */
    private static Map<String, AVLTree> instancePool = new HashMap<>();

    private T          data;
    private AVLTree<T> leftChild;
    private AVLTree<T> rightChild;

    /**
     * 私有化构造方法,使得只能在插入数据的时候得到树集的树根引用
     * @param data
     * @see AVLTree#insert(java.lang.String, java.lang.Comparable)
     */
    private AVLTree(T data) {
        this.data = data;
    }

    /**
     * 计算AVL树的高度
     * @param tree
     * @param <T>
     * @return
     */
    public static <T extends Comparable> int getHeight(AVLTree<T> tree) {
        if (null == tree) {
            return 0;
        }
        int leftHeight  = getHeight(tree.leftChild);
        int rightHeight = getHeight(tree.rightChild);
        return Math.max(leftHeight, rightHeight) + 1;
    }

    /**
     * 先根遍历AVL树,并返回遍历序列
     * @param tree
     * @param <T>
     * @return
     */
    public static <T extends Comparable> List<T> preOrderTraverse(@NotNull AVLTree<T> tree) {
        List<T>           resultList = new ArrayList<>();
        Stack<AVLTree<T>> stack      = new Stack<>();
        AVLTree<T>        t          = tree;
        while (null != t || !stack.isEmpty()) {
            if (null != t) {
                resultList.add(t.data);
                stack.push(t);
                t = t.leftChild;
            } else {
                t = stack.pop();
                t = t.rightChild;
            }
        }
        return resultList;
    }

    /**
     * 中根遍历AVL树,并返回遍历序列
     * @param tree
     * @param <T>
     * @return
     */
    public static <T extends Comparable> List<T> inOrderTraverse(@NotNull AVLTree<T> tree) {
        List<T>           resultList = new ArrayList<>();
        Stack<AVLTree<T>> stack      = new Stack<>();
        AVLTree<T>        t          = tree;
        while (null != t || !stack.isEmpty()) {
            if (null != t) {
                stack.push(t);
                t = t.leftChild;
            } else {
                t = stack.pop();
                resultList.add(t.data);
                t = t.rightChild;
            }
        }
        return resultList;
    }

    /**
     * 后根遍历AVL树,并返回遍历序列
     * @param tree
     * @param <T>
     * @return
     */
    public static <T extends Comparable> List<T> postOrderTraverse(@NotNull AVLTree<T> tree) {
        List<T>           resultList = new ArrayList<>();
        Stack<AVLTree<T>> stack      = new Stack<>();
        AVLTree<T>        t          = tree;
        AVLTree<T>        last       = tree;
        while (null != t || !stack.isEmpty()) {
            if (null != t) {
                stack.push(t);
                t = t.leftChild;
            } else {
                //不弹出栈顶元素
                t = stack.peek();
                if (null == t.rightChild || last == t.rightChild) {
                    resultList.add(t.data);
                    //弹出栈顶元素
                    stack.pop();
                    last = t;
                    t    = null;
                } else {
                    t = t.rightChild;
                }
            }
        }
        return resultList;
    }

    /**
     * 层次遍历AVL树,并返回遍历序列
     * @param tree
     * @param <T>
     * @return
     */
    public static <T extends Comparable> List<T> levelOrderTraverse(@NotNull AVLTree<T> tree) {
        List<T>           resultList = new ArrayList<>();
        Queue<AVLTree<T>> queue      = new LinkedList<>();
        AVLTree<T>        t          = tree;
        queue.offer(t);
        while (!queue.isEmpty()) {
            t = queue.poll();
            resultList.add(t.data);
            if (null != t.leftChild) {
                queue.offer(t.leftChild);
            }
            if (null != t.rightChild) {
                queue.offer(t.rightChild);
            }
        }
        return resultList;
    }

    /**
     * 取出树根池中相应树根的引用。若树根池为空或其中无对应键值,则返回null
     * @param <T>
     * @param treeName
     * @return
     */
    public static <T extends Comparable> AVLTree<T> getInstance(String treeName) {
        return (instancePool.isEmpty() || !instancePool.containsKey(treeName)) ? null : (AVLTree<T>) instancePool.get(treeName);
    }

    /**
     * 指定一个键,该键对应的AVL树中插入数据,并返回树根池中键对应的树根引用
     * @param treeName
     * @param data
     * @param <T>
     * @return
     */
    public static <T extends Comparable> AVLTree<T> insert(String treeName, @NotNull T data) {
        if (null == getInstance(treeName)) {
            AVLTree<T> tree = new AVLTree<>(data);
            instancePool.put(treeName, tree);
            return tree;
        } else {
            return insert(getInstance(treeName), treeName, data);
        }
    }

    /**
     * 如果AVL树根池中存在该键的引用,则递归查找合适的位置插入新的数据,并返回树根池中对应的树根引用
     * 如果由于树根的不平衡导致的旋转,则键在树根池中对应的树根引用也将指向新的树根
     * @param tree
     * @param treeName
     * @param data
     * @param <T>
     * @return
     */
    private static <T extends Comparable> AVLTree<T> insert(AVLTree<T> tree, String treeName, @NotNull T data) {
        if (null == tree) {
            tree = new AVLTree<>(data);
        } else if (data.compareTo(tree.data) < 0) {
            //向左子树寻找插入位置
            tree.leftChild = insert(tree.leftChild, treeName, data);
            //维持平衡
            if (!isBalanced(tree)) {
                AVLTree<T> temp = tree;
                if (data.compareTo(tree.leftChild.data) < 0) {
                    //如果新树的值比不平衡树的左孩子值小,则该不平衡树为LL型
                    tree = LL(tree);
                } else if (data.compareTo(tree.leftChild.data) > 0) {
                    //如果新树的值比不平衡树的左孩子值大,则该不平衡树为LR型
                    tree = LR(tree);
                }
                //如果旋转导致了树根的变化,那么需要更新树根池中的引用
                if (temp == getInstance(treeName)) {
                    instancePool.put(treeName, tree);
                }
            }
        } else if (data.compareTo(tree.data) > 0) {
            //向右子树寻找插入位置
            tree.rightChild = insert(tree.rightChild, treeName, data);
            if (!isBalanced(tree)) {
                AVLTree<T> temp = tree;
                if (data.compareTo(tree.rightChild.data) < 0) {
                    tree = RL(tree);
                } else if (data.compareTo(tree.rightChild.data) > 0) {
                    tree = RR(tree);
                }
                if (temp == getInstance(treeName)) {
                    instancePool.put(treeName, tree);
                }
            }
        }
        return tree;
    }

    /**
     * 判断该AVL树是否平衡
     * @param tree
     * @param <T>
     * @return
     */
    private static <T extends Comparable> boolean isBalanced(AVLTree<T> tree) {
        return Math.abs(getHeight(tree.leftChild) - getHeight(tree.rightChild)) < 2;
    }

    /**
     * 【左-左型】需要进行单次右旋
     * @param unbalancedTree
     * @param <T>
     * @return
     */
    private static <T extends Comparable> AVLTree<T> LL(AVLTree<T> unbalancedTree) {
        //不平衡树的左节点成为新平衡树
        AVLTree<T> balancedTree = unbalancedTree.leftChild;
        //原【新平衡树】的右子树成为原【不平衡树】的左子树
        unbalancedTree.leftChild = balancedTree.rightChild;
        //原【不平衡树】成为【新平衡树】的右子树
        balancedTree.rightChild = unbalancedTree;
        return balancedTree;
    }

    /**
     * 【右-右型】需要进行单次左旋
     * @param unbalancedTree
     * @param <T>
     * @return
     */
    private static <T extends Comparable> AVLTree<T> RR(AVLTree<T> unbalancedTree) {
        //不平衡树的右节点成为新平衡树
        AVLTree<T> balancedTree = unbalancedTree.rightChild;
        //原【新平衡树】的左子树成为原【不平衡树】的右子树
        unbalancedTree.rightChild = balancedTree.leftChild;
        //原【不平衡树】成为【新平衡树】的左子树
        balancedTree.leftChild = unbalancedTree;
        return balancedTree;
    }

    /**
     * 【上左-下右型】需要先单次右旋,再单次左旋
     * @param unbalancedTree
     * @param <T>
     * @return
     */
    private static <T extends Comparable> AVLTree<T> LR(AVLTree<T> unbalancedTree) {
        //先对不平衡树的左子树进行单次左旋),转化为【左-左型】
        unbalancedTree.leftChild = RR(unbalancedTree.leftChild);
        //再对不平衡树进行单次右旋
        return LL(unbalancedTree);
    }

    /**
     * 【上右-下左型】需要先单次左旋,再单次右旋
     * @param unbalancedTree
     * @param <T>
     * @return
     */
    private static <T extends Comparable> AVLTree<T> RL(AVLTree<T> unbalancedTree) {
        //先对不平衡树的右子树进行单次右旋,转化为【右-右型】
        unbalancedTree.rightChild = LL(unbalancedTree.rightChild);
        //再对不平衡树进行单次单次左旋
        return RR(unbalancedTree);
    }
}

AVL树 --> test

package xyz.mstar.main;

import xyz.mstar.tree.AVLTree;

import java.util.LinkedHashSet;
import java.util.Random;
import java.util.Set;

/**
 * @author IceFery
 */
public class Main {
    public static void main(String[] args) {
        test("root1", 10, 100);
        test("root2", 15, 100);
        test("root3", 20, 20);
    }

    private static void test(String treeName, int amount, int range) {
        AVLTree<Integer> tree   = null;
        Random           random = new Random();
        Set<Integer>     src    = new LinkedHashSet<>(amount);
        while (src.size() < amount) {
            int a = random.nextInt(range);
            src.add(a);
        }
        for (Integer a : src) {
            tree = AVLTree.insert(treeName, a);
        }
        display(src, tree);
    }

    private static void display(Set<Integer> src, AVLTree<Integer> tree) {
        System.out.print("集合中原数据:");
        for (int a : src) {
            System.out.printf("%-4d", a);
        }
        System.out.println();

        System.out.print("中根遍历序列:");
        for (Integer b : AVLTree.inOrderTraverse(tree)) {
            System.out.printf("%-4d", b);
        }
        System.out.println();

        System.out.print("后根遍历序列:");
        for (Integer b : AVLTree.postOrderTraverse(tree)) {
            System.out.printf("%-4d", b);
        }
        System.out.println();
        System.out.println();
    }
}

在这里插入图片描述





四、红黑树


红黑树的性质

  1. 性质1:节点是红色或黑色。

  2. 性质2:根节点是黑色。

  3. 性质3:每个叶结点(NULL)是黑色的

  4. 性质4:每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

  5. 性质5:从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

关于性质3,在TreeMap源码中,在构建Entry节点时,颜色已经默认为黑色
然后TreeMap源码巧妙的划归了很多种情况,大部分的调整基本都是针对性质4的,简单点说就是不能出现连续的红色节点

红黑树的定义

public class RBTree<K extends Comparable, V> {
    static class Node<K, V> {
        K          key;
        V          value;
        Node<K, V> left;
        Node<K, V> right;
        Node<K, V> parent;
        Color      color = Color.BLACK;

        Node(K key, V value, Node<K, V> parent) {
            this.key    = key;
            this.value  = value;
            this.parent = parent;
        }

        V setValue(V value) {
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }
    }
    
	private Node<K, V> root     = null;
    private int        size     = 0;
    private int        modCount = 0;
}

红黑树的getter和setter(包括静态内部类中的setValue()方法)

	private static <K, V> Color colorOf(Node<K, V> p) {
        return (p == null) ? Color.BLACK : p.color;
    }

    private static <K, V> Node<K, V> parentOf(Node<K, V> p) {
        return (p == null) ? null : p.parent;
    }

    private static <K, V> Node<K, V> leftOf(Node<K, V> p) {
        return (p == null) ? null : p.left;
    }

    private static <K, V> Node<K, V> rightOf(Node<K, V> p) {
        return (p == null) ? null : p.right;
    }

    private static <K, V> void setColor(Node<K, V> p, Color color) {
        if (p != null) {
            p.color = color;
        }
    }

红黑树的旋转算法

这里的旋转算法和AVL树中的旋转算法略有不同,虽然都是表达那么个意思。

  1. AVL树的平衡是以平衡因子是否不大于1为标准;而红黑树的自平衡是以判断是否破坏了5条性质为标准
  2. AVL树按不平衡的那个树集的形状分为4种形状的调整方案;红黑树中但凡有两个节点,都可以进行旋转
  3. AVL树旋转算法的参数是不平衡的那颗树集的树根;红黑树的参数(图容易表述)

在这里插入图片描述                      

在这里插入图片描述

	private void rotateLeft(Node<K, V> p) {
        if (p != null) {
            Node<K, V> r = p.right;
            p.right = r.left;
            if (r.left != null) {
                r.left.parent = p;
            }
            r.parent = p.parent;
            if (p.parent == null) {
                root = r;
            } else if (p.parent.left == p) {
                //如果p的父节点是LR型,则原p的父节点将左旋转为LL型
                p.parent.left = r;
            } else {
                //如果p的父节点是RR型,则原p的父节点将左旋转为RL型
                p.parent.right = r;
            }
            r.left   = p;
            p.parent = r;
        }
    }
	private void rotateRight(Node<K, V> p) {
        if (p != null) {
            Node<K, V> l = p.left;
            p.left = l.right;
            if (l.right != null) {
                l.right.parent = p;
            }
            l.parent = p.parent;
            if (p.parent == null) {
                root = l;
            } else if (p.parent.right == p) {
                p.parent.right = l;
            } else {
                p.parent.left = l;
            }
            l.right  = p;
            p.parent = l;
        }
    }

红黑树的 put 及其调整方法

红黑树的插入需要调整的情况可分为这三种

  1. 情况1:新节点是根节点。(破坏性质2)
  2. 情况2:新节点是红色,父节点是红色,叔叔节点是红色。(破坏性质4)
  3. 情况3:新节点是红色,父节点是红色,叔叔节点是黑色。(破坏性质4)

在这里插入图片描述在这里插入图片描述

	public V put(K key, V value) {
        Node<K, V> t = root;
        if (t == null) {
            /*
             * 【情况1】:插入根节点,违反性质2(根节点是黑色的)
             * 只需将根节点涂黑,不需要调整。(对应调整算法末尾的强制涂黑根节点)
             */
            //节点默认颜色为黑色,不需要再涂黑
            root = new Node<>(key, value, null);

            size = 1;
            modCount++;
            return null;
        }

        //寻找插入位置
        int        cmp;
        Node<K, V> parent;
        do {
            parent = t;
            cmp    = key.compareTo(t.key);
            if (cmp < 0) {
                t = t.left;
            } else if (cmp > 0) {
                t = t.right;
            } else {
                //如果键重复则返回键原来对应的值,并以新数据覆盖
                return t.setValue(value);
            }
        } while (t != null);

        //插入新的一个键值对
        Node<K, V> e = new Node<>(key, value, parent);
        if (cmp < 0) {
            parent.left = e;
        } else {
            parent.right = e;
        }

        //修复红黑树性质
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }
	private void fixAfterInsertion(Node<K, V> x) {
        /*
         * 红黑树插入的新节点都是红色节点,因为插入红色节点比插入黑色节点容易调整
         * 如果插入黑色节点,直接就破环了性质5
         */
        x.color = Color.RED;

        while (x != null && x != root && x.parent.color == Color.RED) {
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                //叔叔节点
                Node<K, V> y = rightOf(parentOf(parentOf(x)));

                if (colorOf(y) == Color.RED) {
                    /*
                     * 【情况2】:当前节点的父节点是红色,叔叔节点是红色
                     * (1)将父节点和叔叔节点涂黑,爷爷节点涂红
                     * (2)把当前节点指向爷爷节点,从新的当前节点重新开始算法
                     */
                    setColor(parentOf(x), Color.BLACK);
                    setColor(y, Color.BLACK);
                    setColor(parentOf(parentOf(x)), Color.RED);
                    x = parentOf(parentOf(x));
                } else {
                    /*
                     * 【情况3】:当前节点的父节点是红色,叔叔节点是黑色
                     * (1)如果当前节点是父节点的右节点(即其爷爷节点代表的那棵树构成LR型),则需要以父节点进行左转(使爷爷代表的那棵树节点构成LL型)
                     * (2)父节点涂黑,爷爷节点涂红
                     * (3)以爷爷节点右旋
                     */
                    if (x == rightOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateLeft(x);
                    }
                    setColor(parentOf(x), Color.BLACK);
                    setColor(parentOf(parentOf(x)), Color.RED);
                    rotateRight(parentOf(parentOf(x)));
                }
            } else {
                Node<K, V> y = leftOf(parentOf(parentOf(x)));
                if (colorOf(y) == Color.RED) {
                    setColor(parentOf(x), Color.BLACK);
                    setColor(y, Color.BLACK);
                    setColor(parentOf(parentOf(x)), Color.RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == leftOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateRight(x);
                    }
                    setColor(parentOf(x), Color.BLACK);
                    setColor(parentOf(parentOf(x)), Color.RED);
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        //将根节点强制为黑色
        root.color = Color.BLACK;
    }

红黑树的 remove 及其调整算法

删除节点只有这3种情况:

  1. 情况1:待删除节点既有左孩子又有右孩子(找到后继节点(大于该节点的最小值的节点)来替代占位,转化为情况2)
  2. 情况2:只有一个子树(用子节点替代,直接删除,如果删除的黑色节点,则需要进行后续调整)
  3. 情况3:没有子树(直接删除)

只有删除黑色节点时才需要调整:

  1. 情况1:删除节点和孩子节点(替代节点)都是黑色,还可以再分为4种情况:

    1. 兄弟节点是红色的

    2. 兄弟节点是黑色的,并且兄弟节点的两个子节点都是黑色的

    3. 兄弟节点是黑色的,并且兄弟节点的左孩子为红,右孩子为黑

    4. 兄弟节点是黑色的,兄弟节点的右孩子为红

  2. 情况2:删除节点是黑色,孩子节点(替代节点)是红色

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

	public V remove(@NotNull K key) {
        Node<K, V> p = getNode(key);
        if (p == null) {
            return null;
        }
        V oldValue = p.value;
        deleteNode(p);
        return oldValue;
    }
	/**
     * 返回节点的后继,即大于该节点的最小值
     * 规则是:右分支最左边的节点,或者左分支最右边的节点
     * 红黑树其实还是二叉排序树,所以将红黑树压平后(或者中根遍历)后就是一个有序的数组。
     * 所以所谓的后继节点就是:寻找大于该节点的所以节点中,值最小的节点
     * 所以不管怎样,对于一个节点而言,下一个节点一定是在树的右边,区别在于:比当前树更高还是更低
     * @param t
     * @param <K>
     * @param <V>
     * @return
     */
	private static <K, V> Node<K, V> successor(Node<K, V> t) {
        if (t == null) {
            return null;
        } else if (t.right != null) {
            //如果t存在右分支,则向右分支的左子树查找替代节点
            Node<K, V> p = t.right;
            while (p.left != null) {
                p = p.left;
            }
            return p;
        } else {
            /*
             * 如果t不存在右分支,则要么有比该节点大的祖先节点,要么当前节点就是最大值即该节点没有后继节点
             * 通过while循环回溯,如果父节点一直都是通过右子树直到遍历到根,那么说明该节点就是最大值
             * 而如果其中有一个父节点是从左子树进入到的,即ch == p.left 那么这个父节点就是我们要找的值
             */
            Node<K, V> p  = t.parent;
            Node<K, V> ch = t;
            while (p != null && ch == p.right) {
                ch = p;
                p  = p.parent;
            }
            return p;
        }
    }
private void deleteNode(Node<K, V> p) {
        modCount--;
        size--;
        if (p.left != null && p.right != null) {
            /*
             * 【删除情况1】:待删除节点既有左孩子又有右孩子
             * (1)找到待删除节点p的后继节点s
             * (2)将后继节点s的key-value拷贝到p节点
             * (3)此时待删除节点p保持不变,而后继节点s必然最多只有右子树,则删除后继节点的操作可转化为删除情况2
             */
            //
            Node<K, V> s = successor(p);
            p.key   = s.key;
            p.value = s.value;
            p       = s;
        }

        //取p节点的字节点作为替代节点
        Node<K, V> replacement = (p.left != null) ? p.left : p.right;
        if (replacement != null) {
            /*
             * 【删除情况2】:只有一个子树
             * (1)直接直接用替代节点替换到删除节点的位置
             * (2)如果删除节点为黑色,则需要递归调整
             */
            replacement.parent = p.parent;
            if (p.parent == null) {
                root = replacement;
            } else if (p == p.parent.left) {
                p.parent.left = replacement;
            } else {
                p.parent.right = replacement;
            }
            //置空引用
            p.left   = null;
            p.right  = null;
            p.parent = null;

            if (p.color == Color.BLACK) {
                //replacement是删除节点的子节点
                fixAfterDeletion(replacement);
            }
        } else if (p.parent == null) {
            /*
             * 【删除情况2】:只有根节点
             * 直接删除根节点
             */
            root = null;
        } else {
            /*
             * 【删除情况3】:没有子树
             * 如果删除节点为黑色,则需要递归调整
             */
            //因为没有可替换节点,则需要先进行调整再进行删除,否则删除后就找不到父节点,无法调整
            if (p.color == Color.BLACK) {
                fixAfterDeletion(p);
            }
            //置空父节点对p节点的引用
            if (p.parent != null) {
                if (p == p.parent.left) {
                    p.parent.left = null;
                } else if (p == p.parent.right) {
                    p.parent.right = null;
                }
                p.parent = null;
            }
        }
    }
private void fixAfterDeletion(Node<K, V> x) {
        //如果被删除节点是红色的。则不需要调整,直接用它的黑色子节点替换即可,不用进入此方法

        while (x != root && colorOf(x) == Color.BLACK) {
            /*
             * 【调整情况1】:删除节点和孩子节点(替代节点)都是黑色
             * 可分为3种小情况
             */
            if (x == leftOf(parentOf(x))) {
                //兄弟节点
                Node<K, V> sib = rightOf(parentOf(x));
                if (colorOf(sib) == Color.RED) {
                    /*
                     * 【调整情况1-1】:兄弟节点是红色的
                     * (1)兄弟节点涂黑
                     * (2)父节点涂红
                     * (3)以父节点左旋。
                     * (4)这样就能确保新的兄弟节点为黑色,为下一步调整做好准备
                     */
                    setColor(sib, Color.BLACK);
                    setColor(parentOf(x), Color.RED);
                    rotateLeft(parentOf(x));
                    sib = rightOf(parentOf(x));
                }
                if (colorOf(leftOf(sib)) == Color.BLACK && colorOf(rightOf(sib)) == Color.BLACK) {
                    /*
                     * 【调整情况1-2】:兄弟节点是黑色的,并且兄弟节点的两个子节点都是黑色的
                     * (1)兄弟节点涂红
                     * (2)递归调整父节点
                     */
                    setColor(sib, Color.RED);
                    x = parentOf(x);
                } else {
                    if (colorOf(rightOf(sib)) == Color.BLACK) {
                        /*
                         * 【调整情况1-3】:兄弟节点是黑色的,兄弟节点的左孩子为红,右孩子为黑
                         * (1)兄弟节点的左孩子涂黑
                         * (2)兄弟节点涂红
                         * (3)以兄弟节点右旋,转化为情况1-4
                         */
                        setColor(leftOf(sib), Color.BLACK);
                        setColor(sib, Color.RED);
                        rotateRight(sib);
                        sib = rightOf(parentOf(x));
                    }
                    /*
                     * 【调整情况1-4】:兄弟节点是黑色的,兄弟节点的右孩子为红
                     * (1)兄弟节点涂为其父节点的颜色
                     * (2)父节点涂黑
                     * (3)兄弟节点的右孩子涂黑
                     * (4)以父节点左旋
                     * (5)调整完毕,跳出循环
                     */
                    setColor(sib, colorOf(parentOf(x)));
                    setColor(parentOf(x), Color.BLACK);
                    setColor(rightOf(sib), Color.BLACK);
                    rotateLeft(parentOf(x));

                    //将x设置成root跳出循环,此时x原来的引用并没有改变
                    x = root;
                }
            } else {
                Node<K, V> sib = leftOf(rightOf(x));
                if (colorOf(sib) == Color.RED) {
                    setColor(sib, Color.BLACK);
                    setColor(parentOf(x), Color.RED);
                    rotateRight(parentOf(x));
                    sib = leftOf(parentOf(x));
                }
                if (colorOf(rightOf(sib)) == Color.BLACK && colorOf(rightOf(sib)) == Color.BLACK) {
                    setColor(sib, Color.RED);
                    x = parentOf(x);
                } else {
                    if (colorOf(leftOf(sib)) == Color.BLACK && colorOf(leftOf(sib)) == Color.BLACK) {
                        setColor(rightOf(sib), Color.BLACK);
                        setColor(sib, Color.RED);
                        rotateLeft(sib);
                        sib = leftOf(parentOf(x));
                    }
                    setColor(sib, colorOf(parentOf(x)));
                    setColor(parentOf(x), Color.BLACK);
                    setColor(leftOf(sib), Color.BLACK);
                    rotateRight(parentOf(x));
                    x = root;
                }
            }
        }
        /*
         * 【调整情况2】:删除节点是黑色,孩子节点(替代节点)是红色
         * 孩子节点涂黑即可
         */
        setColor(x, Color.BLACK);
    }

红黑树的mini版完整代码

package xyz.mstar.rb;
import com.sun.istack.internal.NotNull;
import java.awt.*;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author IceFery
 */
public class RBTree<K extends Comparable, V> {
    static class Node<K, V> {
        K          key;
        V          value;
        Node<K, V> left;
        Node<K, V> right;
        Node<K, V> parent;
        Color      color = Color.BLACK;

        Node(K key, V value, Node<K, V> parent) {
            this.key    = key;
            this.value  = value;
            this.parent = parent;
        }

        V setValue(V value) {
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }
    }

    private static <K, V> Color colorOf(Node<K, V> p) {
        return (p == null) ? Color.BLACK : p.color;
    }

    private static <K, V> Node<K, V> parentOf(Node<K, V> p) {
        return (p == null) ? null : p.parent;
    }

    private static <K, V> Node<K, V> leftOf(Node<K, V> p) {
        return (p == null) ? null : p.left;
    }

    private static <K, V> Node<K, V> rightOf(Node<K, V> p) {
        return (p == null) ? null : p.right;
    }

    private static <K, V> void setColor(Node<K, V> p, Color color) {
        if (p != null) {
            p.color = color;
        }
    }

    /**
     * 返回节点的后继,即大于该节点的最小值
     * 规则是:右分支最左边的节点,或者左分支最右边的节点
     * 红黑树其实还是二叉排序树,所以将红黑树压平后(或者中根遍历)后就是一个有序的数组。
     * 所以所谓的后继节点就是:寻找大于该节点的所以节点中,值最小的节点
     * 所以不管怎样,对于一个节点而言,下一个节点一定是在树的右边,区别在于:比当前树更高还是更低
     * @param t
     * @param <K>
     * @param <V>
     * @return
     */
    private static <K, V> Node<K, V> successor(Node<K, V> t) {
        if (t == null) {
            return null;
        } else if (t.right != null) {
            //如果t存在右分支,则向右分支的左子树查找替代节点
            Node<K, V> p = t.right;
            while (p.left != null) {
                p = p.left;
            }
            return p;
        } else {
            /*
             * 如果t不存在右分支,则要么有比该节点大的祖先节点,要么当前节点就是最大值即该节点没有后继节点
             * 通过while循环回溯,如果父节点一直都是通过右子树直到遍历到根,那么说明该节点就是最大值
             * 而如果其中有一个父节点是从左子树进入到的,即ch == p.left 那么这个父节点就是我们要找的值
             */
            Node<K, V> p  = t.parent;
            Node<K, V> ch = t;
            while (p != null && ch == p.right) {
                ch = p;
                p  = p.parent;
            }
            return p;
        }
    }

    private Node<K, V> root     = null;
    private int        size     = 0;
    private int        modCount = 0;

    public Map<K, V> preOrderTraverse() {
        Map<K, V> resultMap = new LinkedHashMap<>();
        resultMap = preOrderTraverse(resultMap, root);
        return resultMap;
    }

    public Map<K, V> inOrderTraverse() {
        Map<K, V> resultMap = new LinkedHashMap<>();
        resultMap = inOrderTraverse(resultMap, root);
        return resultMap;
    }

    public Map<K, V> postOrderTraverse() {
        Map<K, V> resultMap = new LinkedHashMap<>();
        resultMap = postOrderTraverse(resultMap, root);
        return resultMap;
    }

    private Map<K, V> preOrderTraverse(Map<K, V> resultMap, Node<K, V> node) {
        if (node == null) {
            return resultMap;
        }
        resultMap.put(node.key, node.value);
        resultMap = preOrderTraverse(resultMap, node.left);
        resultMap = preOrderTraverse(resultMap, node.right);
        return resultMap;
    }


    private Map<K, V> inOrderTraverse(Map<K, V> resultMap, Node<K, V> node) {
        if (node == null) {
            return resultMap;
        }
        resultMap = preOrderTraverse(resultMap, node.left);
        resultMap.put(node.key, node.value);
        resultMap = preOrderTraverse(resultMap, node.right);
        return resultMap;
    }

    private Map<K, V> postOrderTraverse(Map<K, V> resultMap, Node<K, V> node) {
        if (node == null) {
            return resultMap;
        }
        resultMap = preOrderTraverse(resultMap, node.left);
        resultMap = preOrderTraverse(resultMap, node.right);
        resultMap.put(node.key, node.value);
        return resultMap;
    }

    public V get(K key) {
        Node<K, V> p = getNode(key);
        return (p == null) ? null : p.value;
    }

    /**
     * 向红黑树中插入一个键值对,并返回该键原来对应的值
     * 如果键重复,则新数据将覆盖原来的键值
     * @param key
     * @param value
     * @return
     */
    public V put(K key, V value) {
        Node<K, V> t = root;
        if (t == null) {
            /*
             * 【情况1】:插入根节点,违反性质2(根节点是黑色的)
             * 只需将根节点涂黑,不需要调整。(对应调整算法末尾的强制涂黑根节点)
             */
            //节点默认颜色为黑色,不需要再涂黑
            root = new Node<>(key, value, null);

            size = 1;
            modCount++;
            return null;
        }

        //寻找插入位置
        int        cmp;
        Node<K, V> parent;
        do {
            parent = t;
            cmp    = key.compareTo(t.key);
            if (cmp < 0) {
                t = t.left;
            } else if (cmp > 0) {
                t = t.right;
            } else {
                //如果键重复则返回键原来对应的值,并以新数据覆盖
                return t.setValue(value);
            }
        } while (t != null);

        //插入新的一个键值对
        Node<K, V> e = new Node<>(key, value, parent);
        if (cmp < 0) {
            parent.left = e;
        } else {
            parent.right = e;
        }

        //修复红黑树性质
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

    /**
     * 修复红黑树的性质
     * @param x
     */
    private void fixAfterInsertion(Node<K, V> x) {
        /*
         * 红黑树插入的新节点都是红色节点,因为插入红色节点比插入黑色节点容易调整
         * 如果插入黑色节点,直接就破环了性质5
         */
        x.color = Color.RED;

        while (x != null && x != root && x.parent.color == Color.RED) {
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                //叔叔节点
                Node<K, V> y = rightOf(parentOf(parentOf(x)));

                if (colorOf(y) == Color.RED) {
                    /*
                     * 【情况2】:当前节点的父节点是红色,叔叔节点是红色
                     * (1)将父节点和叔叔节点涂黑,爷爷节点涂红
                     * (2)把当前节点指向爷爷节点,从新的当前节点重新开始算法
                     */
                    setColor(parentOf(x), Color.BLACK);
                    setColor(y, Color.BLACK);
                    setColor(parentOf(parentOf(x)), Color.RED);
                    x = parentOf(parentOf(x));
                } else {
                    /*
                     * 【情况3】:当前节点的父节点是红色,叔叔节点是黑色
                     * (1)如果当前节点是父节点的右节点(即其爷爷节点代表的那棵树构成LR型),则需要以父节点进行左转(使爷爷代表的那棵树节点构成LL型)
                     * (2)父节点涂黑,爷爷节点涂红
                     * (3)以爷爷节点右旋
                     */
                    if (x == rightOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateLeft(x);
                    }
                    setColor(parentOf(x), Color.BLACK);
                    setColor(parentOf(parentOf(x)), Color.RED);
                    rotateRight(parentOf(parentOf(x)));
                }
            } else {
                Node<K, V> y = leftOf(parentOf(parentOf(x)));
                if (colorOf(y) == Color.RED) {
                    setColor(parentOf(x), Color.BLACK);
                    setColor(y, Color.BLACK);
                    setColor(parentOf(parentOf(x)), Color.RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == leftOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateRight(x);
                    }
                    setColor(parentOf(x), Color.BLACK);
                    setColor(parentOf(parentOf(x)), Color.RED);
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        //将根节点强制为黑色
        root.color = Color.BLACK;
    }



    private Node<K, V> getNode(@NotNull K key) {
        Node<K, V> p = root;
        while (p != null) {
            int cmp = key.compareTo(p.key);
            if (cmp < 0) {
                p = p.left;
            } else if (cmp > 0) {
                p = p.right;
            } else {
                return p;
            }
        }
        return null;
    }


    /**
     * 从红黑树中移除一个键值对
     * @param key
     * @return
     */
    public V remove(@NotNull K key) {
        Node<K, V> p = getNode(key);
        if (p == null) {
            return null;
        }
        V oldValue = p.value;
        deleteNode(p);
        return oldValue;
    }

    private void deleteNode(Node<K, V> p) {
        modCount--;
        size--;
        if (p.left != null && p.right != null) {
            /*
             * 【删除情况1】:待删除节点既有左孩子又有右孩子
             * (1)找到待删除节点p的后继节点s
             * (2)将后继节点s的key-value拷贝到p节点
             * (3)此时待删除节点p保持不变,而后继节点s必然最多只有右子树,则删除后继节点的操作可转化为删除情况2
             */
            //
            Node<K, V> s = successor(p);
            p.key   = s.key;
            p.value = s.value;
            p       = s;
        }

        //取p节点的字节点作为替代节点
        Node<K, V> replacement = (p.left != null) ? p.left : p.right;
        if (replacement != null) {
            /*
             * 【删除情况2】:只有一个子树
             * (1)直接直接用替代节点替换到删除节点的位置
             * (2)如果删除节点为黑色,则需要递归调整
             */
            replacement.parent = p.parent;
            if (p.parent == null) {
                root = replacement;
            } else if (p == p.parent.left) {
                p.parent.left = replacement;
            } else {
                p.parent.right = replacement;
            }
            //置空引用
            p.left   = null;
            p.right  = null;
            p.parent = null;

            if (p.color == Color.BLACK) {
                //replacement是删除节点的子节点
                fixAfterDeletion(replacement);
            }
        } else if (p.parent == null) {
            /*
             * 【删除情况2】:只有根节点
             * 直接删除根节点
             */
            root = null;
        } else {
            /*
             * 【删除情况3】:没有子树
             * 如果删除节点为黑色,则需要递归调整
             */
            //因为没有可替换节点,则需要先进行调整再进行删除,否则删除后就找不到父节点,无法调整
            if (p.color == Color.BLACK) {
                fixAfterDeletion(p);
            }
            //置空父节点对p节点的引用
            if (p.parent != null) {
                if (p == p.parent.left) {
                    p.parent.left = null;
                } else if (p == p.parent.right) {
                    p.parent.right = null;
                }
                p.parent = null;
            }
        }
    }

    private void fixAfterDeletion(Node<K, V> x) {
        //如果被删除节点是红色的。则不需要调整,直接用它的黑色子节点替换即可,不用进入此方法

        while (x != root && colorOf(x) == Color.BLACK) {
            /*
             * 【调整情况1】:删除节点和孩子节点(替代节点)都是黑色
             * 可分为3种小情况
             */
            if (x == leftOf(parentOf(x))) {
                //兄弟节点
                Node<K, V> sib = rightOf(parentOf(x));
                if (colorOf(sib) == Color.RED) {
                    /*
                     * 【调整情况1-1】:兄弟节点是红色的
                     * (1)兄弟节点涂黑
                     * (2)父节点涂红
                     * (3)以父节点左旋。
                     * (4)这样就能确保新的兄弟节点为黑色,为下一步调整做好准备
                     */
                    setColor(sib, Color.BLACK);
                    setColor(parentOf(x), Color.RED);
                    rotateLeft(parentOf(x));
                    sib = rightOf(parentOf(x));
                }
                if (colorOf(leftOf(sib)) == Color.BLACK && colorOf(rightOf(sib)) == Color.BLACK) {
                    /*
                     * 【调整情况1-2】:兄弟节点是黑色的,并且兄弟节点的两个子节点都是黑色的
                     * (1)兄弟节点涂红
                     * (2)递归调整父节点
                     */
                    setColor(sib, Color.RED);
                    x = parentOf(x);
                } else {
                    if (colorOf(rightOf(sib)) == Color.BLACK) {
                        /*
                         * 【调整情况1-3】:兄弟节点是黑色的,兄弟节点的左孩子为红,右孩子为黑
                         * (1)兄弟节点的左孩子涂黑
                         * (2)兄弟节点涂红
                         * (3)以兄弟节点右旋,转化为情况1-4
                         */
                        setColor(leftOf(sib), Color.BLACK);
                        setColor(sib, Color.RED);
                        rotateRight(sib);
                        sib = rightOf(parentOf(x));
                    }
                    /*
                     * 【调整情况1-4】:兄弟节点是黑色的,兄弟节点的右孩子为红
                     * (1)兄弟节点涂为其父节点的颜色
                     * (2)父节点涂黑
                     * (3)兄弟节点的右孩子涂黑
                     * (4)以父节点左旋
                     * (5)调整完毕,跳出循环
                     */
                    setColor(sib, colorOf(parentOf(x)));
                    setColor(parentOf(x), Color.BLACK);
                    setColor(rightOf(sib), Color.BLACK);
                    rotateLeft(parentOf(x));

                    //将x设置成root跳出循环,此时x原来的引用并没有改变
                    x = root;
                }
            } else {
                Node<K, V> sib = leftOf(rightOf(x));
                if (colorOf(sib) == Color.RED) {
                    setColor(sib, Color.BLACK);
                    setColor(parentOf(x), Color.RED);
                    rotateRight(parentOf(x));
                    sib = leftOf(parentOf(x));
                }
                if (colorOf(rightOf(sib)) == Color.BLACK && colorOf(rightOf(sib)) == Color.BLACK) {
                    setColor(sib, Color.RED);
                    x = parentOf(x);
                } else {
                    if (colorOf(leftOf(sib)) == Color.BLACK && colorOf(leftOf(sib)) == Color.BLACK) {
                        setColor(rightOf(sib), Color.BLACK);
                        setColor(sib, Color.RED);
                        rotateLeft(sib);
                        sib = leftOf(parentOf(x));
                    }
                    setColor(sib, colorOf(parentOf(x)));
                    setColor(parentOf(x), Color.BLACK);
                    setColor(leftOf(sib), Color.BLACK);
                    rotateRight(parentOf(x));
                    x = root;
                }
            }
        }
        /*
         * 【调整情况2】:删除节点是黑色,孩子节点(替代节点)是红色
         * 孩子节点涂黑即可
         */
        setColor(x, Color.BLACK);
    }

    private void rotateLeft(Node<K, V> p) {
        if (p != null) {
            Node<K, V> r = p.right;
            p.right = r.left;
            if (r.left != null) {
                r.left.parent = p;
            }
            r.parent = p.parent;
            if (p.parent == null) {
                root = r;
            } else if (p.parent.left == p) {
                //如果p的父节点是LR型,则原p的父节点将左旋转为LL型
                p.parent.left = r;
            } else {
                //如果p的父节点是RR型,则原p的父节点将左旋转为RL型
                p.parent.right = r;
            }
            r.left   = p;
            p.parent = r;
        }
    }

    private void rotateRight(Node<K, V> p) {
        if (p != null) {
            Node<K, V> l = p.left;
            p.left = l.right;
            if (l.right != null) {
                l.right.parent = p;
            }
            l.parent = p.parent;
            if (p.parent == null) {
                root = l;
            } else if (p.parent.right == p) {
                p.parent.right = l;
            } else {
                p.parent.left = l;
            }
            l.right  = p;
            p.parent = l;
        }
    }
}

红黑树test

package xyz.mstar.main;
import xyz.mstar.rb.RBTree;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;

/**
 * @author IceFery
 */
public class Test {
    public static void main(String[] args) {
        test(10, 100);
        test(15, 50);
    }

    private static void test(int amount, int range) {
        RBTree<String, Integer> tree   = new RBTree<>();
        Random                  random = new Random();
        Set<Integer>            src    = new LinkedHashSet<>(amount);
        while (src.size() < amount) {
            int a = random.nextInt(range);
            src.add(a);
        }
        for (Integer a : src) {
            tree.put(a.toString(), a);
        }
        display(src, tree);

        //移除1,3号个元素
        Object[] keys = src.toArray();
        System.out.println(tree.remove(keys[1].toString()));
        System.out.println(tree.remove(keys[3].toString()));
        display(src, tree);
    }

    private static <K extends Comparable, V> void display(Set<V> src, RBTree<K, V> tree) {
        System.out.print("集合中原序列:");
        for (V a : src) {
            System.out.printf("%-8s", a.toString());
        }
        System.out.println();

        System.out.print("中根遍历序列:");
        for (Map.Entry entry : tree.inOrderTraverse().entrySet()) {
            System.out.printf("%-8s", entry.toString());
        }
        System.out.println();

        System.out.print("后根遍历序列:");
        for (Map.Entry entry : tree.postOrderTraverse().entrySet()) {
            System.out.printf("%-8s", entry.toString());
        }
        System.out.println();
        System.out.println();
    }
}

在这里插入图片描述

原创文章 41 获赞 34 访问量 3万+

猜你喜欢

转载自blog.csdn.net/XY1790026787/article/details/94307303
今日推荐