一、前言
代码自然会存在很多潜在的bug或者不精炼之处,望好心人指教,教主当不胜感激
这篇博客的目的
二叉树本身是以递归的方式定义的,而现有的大部分二叉树的代码的都是以 树的节点作为二叉树的内部类的方式设计的。
虽然这样的设计的确更容易实现继承,写出更简洁的代码。然而本教主向来对递归比较头疼,所以希望能简单的重构一下二叉树的实现。
代码组织
所以教主希望在接下来代码的组织中
- 最简单的二叉树就用递归的方法来构造
- 先将一些递归算法改写成非递归算法
- 然后再添加一些旋转算法改造成AVL树
- 至于红黑树,本教主觉得其实有点难受的,需要考虑的情况和算法的细节都特别多,所以还是以
TreeMap
的实现为例,构造一棵具有基本功能的最简单的红黑树
引发的问题
-
用递归的方式定义,意味着类中基本都是些静态方法,也就是说,继承和接口在这样的设计中将无法体现用处。
-
由于红黑树各种情况太多,难以找到一个能测试所以情况的一组数据来把图粘上来,但是。数据结构可视化工具 (一个基于canvas的轻小工具)可以用上一用,另外呢数据取自后面的test第二组数据)
目录
二叉树
(1)二叉树定义
(2)二叉树遍历的非递归算法
(3)二叉树高度计算算法
(4)二叉树根据先根遍历创建二叉树算法 (这个算法在大部分教科书上都有,本教主只是稍微让这个算法更加友好,易调用)
(5)二叉树完整代码
AVL树
(1)AVL树定义
(2)AVL树的树根实例存储算法(递归定义导致不再有外部类来提供root字段去保存一个树集的树根实例,那就用树根池来存)
(3)AVL树平衡因子计算算法
(4)AVL树四种旋转算法
(5)AVL树插入算法
(6)AVL树完整代码
红黑树
(1)红黑树的性质
(2)红黑树的定义
(3)红黑树的getter和Setter(参照TreeMap源码,设计红黑树的人巧妙的将处理空指针的逻辑封装在了这些getter和setter中)
(4)红黑树的旋转算法
(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:节点是红色或黑色。
-
性质2:根节点是黑色。
-
性质3:每个叶结点(NULL)是黑色的
-
性质4:每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
-
性质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树中的旋转算法略有不同,虽然都是表达那么个意思。
- AVL树的平衡是以平衡因子是否不大于1为标准;而红黑树的自平衡是以判断是否破坏了5条性质为标准
- AVL树按不平衡的那个树集的形状分为4种形状的调整方案;红黑树中但凡有两个节点,都可以进行旋转
- 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:新节点是根节点。(破坏性质2)
- 情况2:新节点是红色,父节点是红色,叔叔节点是红色。(破坏性质4)
- 情况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:待删除节点既有左孩子又有右孩子(找到后继节点(大于该节点的最小值的节点)来替代占位,转化为情况2)
- 情况2:只有一个子树(用子节点替代,直接删除,如果删除的黑色节点,则需要进行后续调整)
- 情况3:没有子树(直接删除)
只有删除黑色节点时才需要调整:
-
情况1:删除节点和孩子节点(替代节点)都是黑色,还可以再分为4种情况:
-
兄弟节点是红色的
-
兄弟节点是黑色的,并且兄弟节点的两个子节点都是黑色的
-
兄弟节点是黑色的,并且兄弟节点的左孩子为红,右孩子为黑
-
兄弟节点是黑色的,兄弟节点的右孩子为红
-
-
情况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();
}
}