一.简介
红黑树作为一种二叉搜索树的一种实现,红黑树的左右子树高度差可能大于 1。所以红黑树不是严格意义上的平衡二叉树(AVL),但红黑树是黑色节点完美平衡, 其平均统计性能要强于 AVL 。
红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。
1.节点是红色或黑色。
2.根节点是黑色。
3.每个红色节点的两个子节点都是黑色。(红色节点的子节点必须是黑色节点)
4.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
故红黑树是黑色平衡的树,左子树与右子树高度差不会超过2倍。红节点的父节点、子节点只能是黑节点
如果插入节点是黑色则所在路径将多出一个黑色节点,故新插入节点是红色节点(插入/删除红色节点不会改变所在路径黑色节点数量),通过旋转和调整节点颜色保证树的平衡
二.实现
红黑树增加/删除节点时需要保证子树黑节点平衡,有如下情况:
1.插入节点为树的根节点时,把根节点颜色修改为黑色
2.插入节点父节点是黑色节点,由于插入节点是红色节点,不会影响该子树黑色节点数量,故不用任何处理
3.插入节点父节点为红色且父节点的兄弟节点也为红色,父节点、父节点的兄弟节点修改为黑色,父节点的父节点修改为红色,再以父节点的父节点为插入节点递归处理
4.父节点为红色且父节点的兄弟节点为null或为黑节点
由于父节点是红色节点,故父节点的父节点为黑色节点
1)LL型
插入数据父节点的兄弟节点只会为null,如果不为null则父节点的兄弟节点子树与父节点将不是平衡的。
修改父节点为黑色节点,祖父节点修改为红色节点
对祖父节点右旋转,让父节点为子树根节点,由于子树黑节点数量没有发生变化,故旋转后黑节点数量和旋转前一样
2)LR型
父节点左旋转将变为LL型,按LL型处理
3)RR型
修改父节点为黑色,祖父节点修改为红色,对祖父节点左旋转,让父节点为子树根节点,旋转后和旋转前黑色节点数量没有改变,故事黑色节点平衡
4)RL型
父节点右旋转变为RR型,按RR型处理
示例说明:
插入数据:4,3,6,5,7,8,9,10,11
package com.vincent;import java.util.*;public class Main {public static void main(String[] args) throws Exception {RBTree<Integer> tree = new RBTree<>();for(Integer data : Arrays.asList(4,3,6,5,7,8,9,10,11)){tree.add(data);System.out.println(tree.getRoot());}System.out.println();List<Integer> list = tree.infixList();System.out.println(list);tree.delete(list.get(0));list = tree.infixList();System.out.println(list);tree.delete(list.get(list.size()-1));list = tree.infixList();System.out.println(list);tree.delete(list.get(list.size()/2));list = tree.infixList();System.out.println(list);}
}//红黑树
class RBTree<T extends Comparable<T>>{private static final int RED = 0;private static final int BLACK = 1;static class Node<T extends Comparable<T>> {private T item;private int color = RED;private Node<T> left;private Node<T> right;private Node<T> parent;public Node(T item) {this.item = item;}public T getItem(){return item;}/*** 添加节点到当前节点树* @param node 添加的节点*/public void add(Node<T> node){if(node.item.compareTo(this.item) <= 0){if(this.left == null){this.left = node;node.parent = this;}else{this.left.add(node);}}else{if(this.right == null){this.right = node;node.parent = this;}else{this.right.add(node);}}}/*** 通过item查找所在树的结点* @param item* @return*/public Node<T> searchNode(T item){int cmp = item.compareTo(this.item);if(cmp == 0){return this;}Node<T> node = null;if(cmp < 0 && this.left != null && (node=this.left.searchNode(item)) != null){return node;}else if(cmp > 0 && this.right != null && (node=this.right.searchNode(item)) != null){return node;}return null;}/*** 删除节点,如果删除的节点为根节点将有NullPointerException*/public void del(){Node<T> parent = this.parent;if(this.left==null && this.right==null){//删除节点为叶子节点if(parent.left == this){parent.left = null;}else{parent.right = null;}}else if(this.left == null){//删除节点只有右子树if(parent.left == this){parent.left = this.right;}else{parent.right = this.right;}}else if(this.right == null){//删除节点只有左子树if(parent.left == this){parent.left = this.left;}else{parent.right = this.left;}}else{//删除节点有左右节点,从删除节点左子树中寻找最大值Node<T> maxNode = this.left;while(maxNode.right != null){maxNode = maxNode.right;}if(maxNode == this.left){//删除节点左子树的最大值就是删除节点的左节点this.item = maxNode.item;this.left = maxNode.left;}else{maxNode.parent.right = null;this.item = maxNode.item;}}}/*** 节点的中序遍历* @param dst 遍历节点的存放容器*/public void infixList(List<T> dst){if(this.left != null){this.left.infixList(dst);}dst.add(this.item);if(this.right != null){this.right.infixList(dst);}}/*** 该节点树的最大高度* @return*/public int height(){return Math.max(left==null?0:left.height(),right==null?0:right.height())+1;}/*** 对当前节点左旋转操作* @return 返回左旋转操作后子树根节点(即当前节点的右节点)*/public Node<T> leftRotate(){if(this.right==null){return null;}Node<T> right = this.right;this.right = right.left;if(right.left != null) {right.left.parent = this;}right.left = this;right.parent = this.parent;if(this.parent != null) {if (this.parent.left == this) {this.parent.left = right;} else {this.parent.right = right;}}this.parent = right;return right;}/*** 对当前节点右旋转操作* @return 返回右旋转操作后子树根节点(即当前节点的左节点)*/public Node<T> rightRotate(){if(this.left==null){return null;}Node<T> left = this.left;this.left = left.right;if(left.right != null) {left.right.parent = this;}left.right = this;left.parent = this.parent;if(this.parent != null) {if (this.parent.left == this) {this.parent.left = left;} else {this.parent.right = left;}}this.parent = left;return left;}@Overridepublic String toString() {return "Node{" +"item=" + item +'}';}}private Node<T> root;Node<T> getRoot(){return root;}public void add(T item){Node<T> node = new Node<>(item);if(this.root == null){this.root = node;}else{Node<T> old = this.root.searchNode(item);if(old != null){old.item = item;}else{this.root.add(node);}}rebalance(node);}/*** 调整树的平衡性,会依次回溯到父节点* @param node 当前插入的节点*/private void rebalance(Node<T> node){Node<T> parent = node.parent;if(parent == null){//树的根节点node.color = BLACK;return;}else if(parent.color == RED) {//红节点一定有父节点Node<T> pparent = parent.parent;//父节点的兄弟节点Node<T> psibling = pparent.left == parent ? pparent.right : pparent.left;if (psibling != null && psibling.color == RED) {parent.color = psibling.color = BLACK;pparent.color = RED;rebalance(pparent);}else if (psibling == null || psibling.color == BLACK) {Node<T> subRoot = null;if (pparent.left == parent && parent.left == node) {//LL型parent.color = BLACK;pparent.color = RED;subRoot = pparent.rightRotate();} else if (pparent.left == parent && parent.right == node) {//LR型parent.leftRotate();node.color = BLACK;pparent.color = RED;subRoot = pparent.rightRotate();} else if (pparent.right == parent && parent.left == node) {//RL型parent.rightRotate();node.color = BLACK;pparent.color = RED;subRoot = pparent.leftRotate();} else {//RR型parent.color = BLACK;pparent.color = RED;subRoot = pparent.leftRotate();}//设置树的根节点if (subRoot.parent == null) {this.root = subRoot;}}}}public void delete(T item){Node<T> delNode = null;if(root.item.compareTo(item)==0){//删除根节点delNode = root;if(root.left==null && root.right==null){this.root = null;}else if(root.left==null){this.root = root.right;}else if(root.right==null){this.root = root.left;}else{//根节点有左右节点,扫描右子树最小节点Node<T> node = this.root.right;while(node.left!=null){node = node.left;}if(node == this.root.right){this.root.item = node.item;this.root.right = node.right;}else {node.parent.left = null;this.root.item = node.item;}}}else {Node<T> node = this.root.searchNode(item);delNode = node;if (node != null) {node.del();}}if(delNode != null){this.rebalance(delNode);}}public List<T> infixList(){List<T> rst = new ArrayList<>();this.root.infixList(rst);return rst;}public int height(){return this.root.height();}}
三.总结
红黑树的平衡处理相对AVL较为宽松,数据插入/删除时效率相对较高