【樹】——從二叉樹到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();
    }
}

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章