1 红黑树逻辑推导图
https://www.processon.com/view/link/5eddac9ce401fd5f2d94f24d
2 节点类-Node
public class Node<E> {public static final boolean BLACK = true;public static final boolean RED = false;// 每一个新增节点都默认为红色private boolean color = RED;private Node<E> parent;private Node<E> leftChild;private Node<E> rightChild;private E element;public Node(E element,Node<E> parent) {super();this.element = element;this.parent = parent;}public boolean getColor() {return color;}public void setColor(boolean color) {this.color = color;}public Node<E> getParent() {return parent;}public void setParent(Node<E> parent) {this.parent = parent;}public Node<E> getLeftChild() {return leftChild;}public void setLeftChild(Node<E> leftChild) {this.leftChild = leftChild;}public Node<E> getRightChild() {return rightChild;}public void setRightChild(Node<E> rightChild) {this.rightChild = rightChild;}public E getElement() {return element;}public void setElement(E element) {this.element = element;}//是否是左孩子节点public boolean isLeftChild(){return parent != null && this == parent.leftChild;}//是否是右孩子节点public boolean isRightChild(){return parent != null && this == parent.rightChild;}//判断是否是叶子节点public boolean isLeaf(){return leftChild == null && rightChild == null;}//判断是否有两个子节点public boolean hasTwoChild(){return leftChild != null && rightChild != null;}/*** 返回兄弟节点* 如果要拿到叔父节点* 则可以调用 parent.sibling()*/public Node<E> sibling(){if(isLeftChild()){return parent.rightChild;}if(isRightChild()){return parent.leftChild;}//即不是左节点,也不是右节点,则没有兄弟节点//即为根节点return null;}}
3 红黑树实现类-RBTree
import java.util.Comparator;import java.util.LinkedList;import java.util.Queue;public class RBTree<E> {// 比较器,用于比较两个存储对象的大小private Comparator<E> comparator;// 树根private Node<E> root;// 树中元素的多少private int size;public RBTree() {this(null);}public RBTree(Comparator<E> comparator) {this.comparator = comparator;}public void add(E element) {// 校验传入的参数是否为null// 如果为null,则抛出异常elementNotNullCheck(element);// 校验是否创建了树根if (root == null) {root = createNode(element, null);size++;// 新添加节点之后的处理afterAdd(root);return;}// 添加的不是第一个节点Node<E> parent = root;Node<E> node = root;// 用于保存比较的结果// 大于0,则向右子树// 等于0,则有相等的元素// 小于0,则向左子树int cmp = 0;// 遍历找到插入位置的父节点while (node != null) {cmp = compare(element, node.getElement());parent = node;if (cmp > 0) {node = node.getRightChild();} else if (cmp < 0) {node = node.getLeftChild();} else { // 相等node.setElement(element);return;}}// 已经找到了插入位置的父节点 parent// 新建节点Node<E> newNode = createNode(element, parent);if (cmp > 0) {parent.setRightChild(newNode);} else {parent.setLeftChild(newNode);}size++;// 新添加节点之后的处理afterAdd(newNode);}// public E get//删除节点public void remove(E element) {remove(findNode(element));}private void remove(Node<E> node) {if (node == null)return;size--;if (node.hasTwoChild()) { // 度为2的节点// 找到后继节点Node<E> s = successor(node);// 用后继节点的值覆盖度为2的节点的值node.setElement(s.getElement());// 删除后继节点node = s;}// 删除node节点(node的度必然是1或者0)Node<E> replacement = node.getLeftChild() != null ? node.getLeftChild() : node.getRightChild();if (replacement != null) { // node是度为1的节点// 更改parentreplacement.setParent(node.getParent());// 更改parent的left、right的指向if (node.getParent() == null) { // node是度为1的节点并且是根节点root = replacement;} else if (node == node.getParent().getLeftChild()) {node.getParent().setLeftChild(replacement);} else { // node == node.parent.rightnode.getParent().setRightChild(replacement);}// 删除节点之后的处理afterRemove(replacement);} else if (node.getParent() == null) { // node是叶子节点并且是根节点root = null;// 删除节点之后的处理afterRemove(node);} else { // node是叶子节点,但不是根节点if (node == node.getParent().getLeftChild()) {node.getParent().setLeftChild(null);} else { // node == node.parent.rightnode.getParent().setRightChild(null);}// 删除节点之后的处理afterRemove(node);}}// 找到前驱节点protected Node<E> successor(Node<E> node) {if (node == null)return null;// 前驱节点在左子树当中(right.left.left.left....)Node<E> p = node.getRightChild();if (p != null) {while (p.getLeftChild() != null) {p = p.getLeftChild();}return p;}// 从父节点、祖父节点中寻找前驱节点while (node.getParent() != null && node == node.getParent().getRightChild()) {node = node.getParent();}return node.getParent();}// 查找元素对应的Node节点private Node<E> findNode(E element) {Node<E> node = root;while (node != null) {int cmp = compare(element, node.getElement());if (cmp == 0)return node;if (cmp > 0) {node = node.getRightChild();} else { // cmp < 0node = node.getLeftChild();}}return null;}private void afterAdd(Node<E> node) {Node<E> parent = node.getParent();// 一: 如果父节点为null,则表示变动的节点为根节点if (null == parent) {black(node);// 将其置为黑色return;}// 二:如果变动的节点的父节点为黑色,则什么都不用处理if (isBlack(parent)) {return;}// 三:到此处,说明插入节点的父节点为红色Node<E> uncle = parent.sibling();// 祖父节点在这种大的情况下都会要变为红色(仅在最后一步做树根保持黑色的操作)Node<E> grandParent = red(parent.getParent());// 1.叔叔节点为红色,即此时父亲节点和叔叔节点都为红色if (isRed(uncle)) {// 将叔叔节点与父节点染成黑色,将祖父节点染成红色black(parent);black(uncle);// 将祖父节点染成红色,red(grandParent);// 并将其当做新添加节点,向上传递afterAdd(grandParent);return;}// 2.叔叔节点不为红色,那么只能为空(Nil),不能为黑色节点// 父节点是左节点if (parent.isLeftChild()) {// 插入的节点如果是右孩子,则需要进行左旋操作if (node.isRightChild()) {black(node);rotateLeft(parent);}// 插入的节点如果是左孩子,则仅需直接变色else {black(parent);}// 然后以祖父节点为轴,向右旋转rotateRight(grandParent);}// 父节点是右节点else {// 插入的节点如果是左孩子,则先需要右旋操作if (node.isLeftChild()) {black(node);rotateRight(parent);}// 插入的节点如果是右孩子,则将父节点变黑else {black(parent);}// 然后以祖父节点为轴,向左旋转rotateLeft(grandParent);}}private void afterRemove(Node<E> node) {// 如果删除的节点是红色的,那么就不用做任何处理// 用于取代被删除的节点的节点是红色的if (isRed(node)) {black(node);return;}// 删除的是黑色的节点Node<E> parent = node.getParent();// 1.删除的是黑色的根节点if (null == parent)return;// 2.删除的是黑色的叶子节点// 判断是否为左孩子节点boolean left = (parent.getLeftChild() == null) | node.isLeftChild();// 拿到兄弟节点Node<E> sibling = left ? parent.getRightChild() : parent.getLeftChild();if (left) {// 被删除的黑色节点是左孩子,其兄弟节点在右边if (isRed(sibling)) {// 兄弟节点是红色black(sibling);red(parent);rotateLeft(parent);// 经过旋转之后,兄弟节点发生改变sibling = parent.getRightChild();// 后面即可按照兄弟节点为黑色的来处理}// 兄弟节点为黑色if (isBlack(sibling.getLeftChild()) && isBlack(sibling.getRightChild())) {// 兄弟节点的子节点没有红色节点,即:为黑色节点或者为nullboolean parentIsBlack = isBlack(parent);black(parent);red(sibling);if (parentIsBlack) {afterRemove(parent);}} else {// 兄弟节点的子节点中至少有一个为红色节点if (isBlack(sibling.getRightChild())) {// 兄弟节点的右孩子是null,即为黑色的,不为红色// 说明红色节点在左边// 则进行右旋转rotateRight(sibling);// 此时兄弟节点发生了改变sibling = parent.getRightChild();// 此时即可用兄弟节点的红色孩子节点当做在右边处理}// 变色处理(包含了两种情况,将两种情况的颜色统一处理)color(sibling, colorOf(parent));black(sibling.getRightChild());black(parent);rotateLeft(parent);}} else {// 被删除的黑色节点是右孩子,其兄弟节点在左边if (isRed(sibling)) {// 兄弟节点是红色black(sibling);red(parent);rotateRight(parent);// 经过旋转之后,兄弟节点发生改变sibling = parent.getLeftChild();// 后面即可按照兄弟节点为黑色的来处理}// 兄弟节点为黑色if (isBlack(sibling.getLeftChild()) && isBlack(sibling.getRightChild())) {// 兄弟节点的子节点没有红色节点,即:为黑色节点或者为nullboolean parentIsBlack = isBlack(parent);black(parent);red(sibling);if (parentIsBlack) {afterRemove(parent);}} else {// 兄弟节点的子节点中至少有一个为红色节点if (isBlack(sibling.getLeftChild())) {// 兄弟节点的左孩子是null,即为黑色的,不为红色// 则进行左旋转rotateLeft(sibling);// 此时兄弟节点发生了改变sibling = parent.getLeftChild();// 此时即可用兄弟节点的红色孩子节点当做在左边处理}// 变色处理(包含了两种情况,将两种情况的颜色统一处理)color(sibling, colorOf(parent));black(sibling.getLeftChild());black(parent);rotateRight(parent);}}}protected Node<E> createNode(E element, Node<E> parent) {return new Node<>(element, parent);}// 元素不能为null,否则就抛出异常,中断程序private void elementNotNullCheck(E element) {if (element == null) {throw new IllegalArgumentException("element must not be null");}}// 返回值等于0,代表e1和e2相等;返回值大于0,代表e1大于e2;返回值小于于0,代表e1小于e2@SuppressWarnings("unchecked")private int compare(E e1, E e2) {if (comparator != null) {return comparator.compare(e1, e2);}return ((Comparable<E>) e1).compareTo(e2);}/*** ========辅助方法============*/// ==== 旋转// 左旋转private void rotateLeft(Node<E> grand) {Node<E> parent = grand.getRightChild();Node<E> child = parent.getLeftChild();grand.setRightChild(child);parent.setLeftChild(grand);afterRotate(grand, parent, child);}// 右旋转private void rotateRight(Node<E> grand) {Node<E> parent = grand.getLeftChild();Node<E> child = parent.getRightChild();grand.setLeftChild(child);parent.setRightChild(grand);afterRotate(grand, parent, child);}private void afterRotate(Node<E> grand, Node<E> parent, Node<E> child) {// 让parent称为子树的根节点parent.setParent(grand.getParent());if (grand.isLeftChild()) {grand.getParent().setLeftChild(parent);} else if (grand.isRightChild()) {grand.getParent().setRightChild(parent);} else { // grand是root节点root = parent;}// 更新child的parentif (child != null) {child.setParent(grand);}// 更新grand的parentgrand.setParent(parent);}// ==== 染色//将节点设置为黑色public Node<E> black(Node<E> node) {return color(node, Node.BLACK);}//将节点设置为红色public Node<E> red(Node<E> node) {return color(node, Node.RED);}//设置节点颜色private Node<E> color(Node<E> node, boolean color) {if (null != node) {node.setColor(color);}return node;}// ========== 判断颜色 =============//判断是否为黑色public boolean isBlack(Node<E> node) {return colorOf(node) == Node.BLACK;}//判断是否为红色public boolean isRed(Node<E> node) {return colorOf(node) == Node.RED;}//获取颜色,如果节点为null,返回黑色private boolean colorOf(Node<E> node) {return node == null ? Node.BLACK : node.getColor();}/*** 遍历*/// 遍历===前序遍历public void preOrder() {System.out.println("前序遍历");preOrder(root);}// 遍历===前序遍历private void preOrder(Node<E> node) {if (null == node)return;System.out.println((node.getColor() ? "BLACK" : "RED") + node.getElement().toString());preOrder(node.getLeftChild());preOrder(node.getRightChild());}// 遍历===中序遍历public void inOrder() {System.out.println("中序遍历");inOrder(root);}// 遍历===中序遍历private void inOrder(Node<E> node) {if (null == node)return;inOrder(node.getLeftChild());System.out.println((node.getColor() ? "BLACK" : "RED") + node.getElement().toString());inOrder(node.getRightChild());}// 遍历===后序遍历public void afterOrder() {System.out.println("后序遍历");afterOrder(root);}// 遍历===后序遍历private void afterOrder(Node<E> node) {if (null == node)return;afterOrder(node.getLeftChild());afterOrder(node.getRightChild());System.out.println((node.getColor() ? "BLACK" : "RED") + node.getElement().toString());}// 遍历===层序遍历public void levelOrder() {System.out.println("层序遍历");levelOrder(root);}// 遍历===层序遍历private void levelOrder(Node<E> node) {if(root == null){System.out.println("树为空");return;}Queue<Node<E>> queue = new LinkedList<>();queue.offer(root);while (!queue.isEmpty()) {Node<E> head = queue.poll();if (head.getLeftChild() != null) {queue.offer(head.getLeftChild());}if (head.getRightChild() != null) {queue.offer(head.getRightChild());}System.out.println((head.getColor() ? "BLACK" : "RED") + head.getElement().toString());}}}
