红黑树是AVL树的变种,对红黑树的操作最坏情况下花费O(logN)的时间。相较于AVL树,红黑树更能用一种非递归的方式实现插入。红黑树是非严格的平衡二叉树,而AVL树是严格的平衡二叉树。
红黑树具有以下特征:
1、每个节点要么是红色,要么是黑色。
2、根节点是黑色。
3、NULL节点为黑色。
4、如果节点是红色,那么子节点必须是黑色。
5、从一个节点到一个null引用的路径必须包含相同的黑色节点数。
接着来看一下如何插入数据:
自底向上的插入
为了维持第5个特点,所以插入的数据必然要是红色。
所谓自底向上的插入,就是先将红色节点插入到树中,再从插入点,逐步向上解决冲突。
①第一种情况:如果插入后,父节点是黑色,那么旧完成插入。
②第二次情况:如果插入后,父节点是红色,父节点的兄弟节点是黑色,那么进行一次单旋转或者之字形旋转,如下图所示。
③第三种情况:如果插入后,父节点和它的兄弟节点都是红色。(下图中双圆圈代表红色)加入我们要将79插入,那么我们将父节点和它的兄弟节点都变成黑色,这样保证了两边路径黑色节点的平和,祖父节点变成红色,这样保证了祖父节点和其兄弟节点路径上黑色节点数的一致。加入祖父节点的父节点是红色,那么将祖父节点的父节点的红色向上过滤。
自顶向下的插入
自顶向下的插入时在节点向下寻找插入点的过偶程中,保证父亲的兄弟节点不是红色的,这样就只需要进行一次单旋转或者之字形旋转。在向下的过程中,当看到有一个节点有两个红色儿子,那么将这两个红色儿子变成黑色,该节点变成红色。这样能保持该子树路径上的黑色节点和兄弟子树相同。如果该节点为根节点,那么该节点不需要变成红色。如果该节点的父亲节点也是红色,那么只需要通过旋转就能解除冲突,不需要将红色节点上滤,因为自顶向下的过程中已经保证了两个兄弟节点不可能都为红色。
插入代码
采用非递归形式插入
package com.creat.redblack;
/**
* Created by WHZ on 2017/8/13 0013.
*/
public class RedBlackTree<E extends Comparable<? super E>>{
private RedBlackNode<E> root;
private static class RedBlackNode<E>{
int color;
E element;
RedBlackNode<E> left;
RedBlackNode<E> right;
RedBlackNode<E> father;
static final int BLACK = 1;
static final int RED = 0;
RedBlackNode(E element){
this.element = element;
this.father = null;
this.left = null;
this.right = null;
this.color = RedBlackNode.RED;
}
RedBlackNode(E element, RedBlackNode<E> father){
this.element = element;
this.father = father;
this.left = null;
this.right = null;
this.color = RedBlackNode.RED;
}
RedBlackNode(E element,RedBlackNode<E> father,int color){
this.element = element;
this.father = father;
this.left = null;
this.right = null;
this.color = color;
}
}
public void printTree(){
printTree(root);
}
private void printTree(RedBlackNode<E> node){
if(node != null){
printTree(node.left);
System.out.println(node.element+((node.color==1)?"黑色":"红色"));
printTree(node.right);
}
}
public void insert(E element){
if(root == null){
root = new RedBlackNode<E>(element,null,RedBlackNode.BLACK);
}else {
RedBlackNode<E> point = root;
int compareResult = element.compareTo(point.element);
while(compareResult != 0){
if(isRed(point.left) && isRed(point.right)){
if(point == root){
point.left.color = RedBlackNode.BLACK;
point.right.color = RedBlackNode.BLACK;
}else {
point.color = RedBlackNode.RED;
point.left.color = RedBlackNode.BLACK;
point.right.color = RedBlackNode.BLACK;
if(point.father.color == RedBlackNode.RED){
rotate(point);
}
}
}
if(compareResult > 0 ){
if(point.right == null){
point.right = new RedBlackNode<E>(element,point);
if(point.color == RedBlackNode.RED){
rotate(point.right);
}
break;
}else {
point = point.right;
compareResult = element.compareTo(point.element);
}
}else if (compareResult < 0){
if(point.left == null){
point.left = new RedBlackNode<E>(element,point);
if(point.color == RedBlackNode.RED){
rotate(point.left);
}
break;
}else {
point = point.left;
compareResult = element.compareTo(point.element);
}
}else {
break;
}
}
}
}
private void rotate(RedBlackNode<E> point) {
RedBlackNode<E> father = point.father;
RedBlackNode<E> grandfather = father.father;
RedBlackNode<E> result = null;
if(isLeftNode(father) && isLeftNode(point)){
result = rotateWithLeftChild(grandfather);
}else if(isRightNode(father) && isRightNode(point)){
result = rotateWithRightChild(grandfather);
}else if(isLeftNode(father) && isRightNode(point)){
result = doubleWithLeftChild(grandfather);
}else {
result = doubleWithRightChild(grandfather);
}
if(grandfather == root){
root = result;
}else {
if(isRightNode(result)){
result.father.right = result;
}else {
result.father.left = result;
}
}
}
private RedBlackNode<E> rotateWithLeftChild(RedBlackNode<E> node){
RedBlackNode<E> left = node.left;
node.left = left.right;
left.right = node;
node.color = RedBlackNode.RED;
left.color = RedBlackNode.BLACK;
left.father = node.father;
node.father = left;
if(node.left != null){
node.left.father = node;
}
return left;
}
private RedBlackNode<E> rotateWithRightChild(RedBlackNode<E> node){
RedBlackNode<E> right = node.right;
node.right = right.left;
right.left = node;
node.color = RedBlackNode.RED;
right.color = RedBlackNode.BLACK;
right.father = node.father;
node.father = right;
if(node.right != null){
node.right.father = node;
}
return right;
}
private RedBlackNode<E> doubleWithLeftChild(RedBlackNode<E> node){
node.left = rotateWithRightChild(node.left);
return rotateWithLeftChild(node);
}
private RedBlackNode<E> doubleWithRightChild(RedBlackNode<E> node){
node.right = rotateWithLeftChild(node.right);
return rotateWithRightChild(node );
}
private boolean isRed(RedBlackNode<E> node){
if(node == null){
return false;
}else if(node.color == RedBlackNode.RED){
return true;
}else {
return false;
}
}
private boolean isBlack(RedBlackNode<E> node){
if(node == null){
return true;
}else if(node.color == RedBlackNode.BLACK){
return true;
}else {
return false;
}
}
private boolean isLeftNode(RedBlackNode<E> node){
RedBlackNode<E> father = node.father;
if(father == null){
return false;
}else {
int compareResult = node.element.compareTo(father.element);
if(compareResult < 0){
return true;
}else {
return false;
}
}
}
private boolean isRightNode(RedBlackNode<E> node){
RedBlackNode<E> father = node.father;
if(father == null){
return false;
}else {
int compareResult = node.element.compareTo(father.element);
if(compareResult > 0){
return true;
}else {
return false;
}
}
}
}
测试结果:
package com.creat.redblack;
/**
* Created by Administrator on 2017/8/13 0013.
*/
public class Client {
public static void main(String[] args){
RedBlackTree<Integer> tree = new RedBlackTree<Integer>();
tree.insert(30);
tree.insert(70);
tree.insert(60);
tree.insert(15);
tree.insert(10);
tree.insert(20);
tree.insert(80);
tree.insert(85);
tree.insert(50);
tree.insert(65);
tree.insert(5);
tree.insert(40);
tree.insert(55);
tree.insert(89);
tree.insert(65);
tree.printTree();
}
}
5红色
10黑色
15黑色
20黑色
30红色
40红色
50黑色
55红色
60黑色
65红色
70黑色
80黑色
85黑色
89红色
自顶向下的删除
如果需要删除的节点有两个儿子或者只有一个右儿子,那么用右子树上最小的节点代替它,然后删除这个最小节点;
如果需要删除的节点有个左儿子,那么用左子树上最大的节点代替它,然后删除这个最大节点。
但是我们需要保证被删除的这个树叶为红色,所以我们采用一种办法要保证这个找到的树叶为红色。
X为当前指向的节点,我们要确保X是红的,先从根节点(个人理解:这个根节点应该是需要被替换节点的左子树或者右子树的根节点)向下遍历,如果X节点是红的,那么继续向下找,如果X节点不是红的,那么看他儿子是不是红的,如果儿子是红的,那么继续向下找,那么就行如下三种操作的其中一种:
接着继续寻找,最后找到需要被删除的树叶,因为之前的步骤确保了X为红色或者说X的儿子为红色,所以最后这个树叶可以被安全删除。