代码来自https://book.douban.com/subject/10530466/
<<data structure and algorithm analysis in java>>
/*红黑树是具有下列性质的二叉查找树,是avl树的变种:
* 1.每一个节点或者着成红色(两个圈),或者黑色
* 2.根是黑色的
* 3.如果一个节点是红色的,那么它的子节点必须是黑色的
* 4.从一个节点到一个null引用的每一条路径必须包含相同数目的黑色节点
*/
//对于红黑树的(各种)操作花费O(log N)
/* 插入一个新项时,作为树叶插入。
* 如果插入黑色,违反4,所以只能插入红色。
* 如果父节点是黑色的,结束;
* 父节点是红色的,违反3,所以必须调整(颜色的改变和树的旋转)
*/
/*
* 父节点是红色的:
* 设X是新添加的叶子,P是父节点,S是父节点兄弟
* G是祖父节点
* 1.父节点的兄弟是黑色的(null默认为黑色),
* 此时祖父节点一定是黑色的
* (在图12-10(红色为两个圈)中给出了两种改变方式,图片中以P为G的左儿子
* 还有两种对称的P为G的右儿子,自己写)
* 第一种情形对应P和G之间的单旋转,而第二种情形对应双旋转,该双旋转首先
* 在X和P之间进行,然后在X和G之间进行。当编写程序时,我们必须记录父节点
* 、祖父节点,以及为了重新连接记录曾祖节点。
* 由于旋转后,祖父变成了黑,所以不管曾祖父是怎样的都结束了
* 2.父节点的兄弟是红色的
* 在这种情况下,初始时从子树的根到C的路径上有一个黑色节点。
* 在旋转之后,一定仍然还是只有一个黑色节点。在这两种情况(图片上的)下,
* 在通向C的路径上都有三个节点(新的根,G和S)。由于只有一个可能是黑色的,
* 又由于我们不能有连续的红色节点,于是我们必须把S和字数的新根都涂成红色,
* 而把G(以及第四个节点)涂成黑色。
* 如果曾祖也是红色又怎么样呢?此时,我们可以将这个过程朝着根的方向上滤,
* 就像对B树和二叉堆一样,直到不再有两个相连的红色节点或者到达根(将被重新涂黑)
* 处为止。
*
*
*
*/
/*
* 实际实现我们用自顶向下来做。
* 1.X非根,在向下的过程中看到一个节点X有两个红儿子的时候,可使X呈红色而让儿子变黑(图12-11)
* 2.如果X是根,则X要再变黑
* 3.如果X的父节点也是红的时候,由于是自顶向下做的,可以保证X的父节点兄弟是黑。
* 套用上面父节点是红,父节点兄弟是黑的那种旋转。
*/
/*
* 具体实现,由于要处理根的特殊情况以及大量的旋转和空子树
* 我们使用两个标记节点,header,nullNode
* header是root的根,header的右链是root
* nullNode是指示一个null引用
*/
//
package Red_Black_Tree;
public class RedBlackTree<AnyType extends Comparable<? super AnyType>> {
public RedBlackTree()
{
//注意下面的2、4行
//left,right不是指向null,而是nullNode
//我个人认为是实现了无论从哪个地方插入,都会是nullNode
//便于操作
nullNode=new RedBlackNode<AnyType>(null);
nullNode.left=nullNode.right=nullNode;
header=new RedBlackNode<AnyType>(null);
header.left=header.right=nullNode;
}
private static class RedBlackNode<AnyType>
{
RedBlackNode(AnyType theElement)
{
this(theElement,null,null);
}
RedBlackNode(AnyType theElement,RedBlackNode<AnyType>lt,RedBlackNode<AnyType>rt)
{
element=theElement;
left=lt;
right=rt;
color=RedBlackTree.BLACK;
}
AnyType element;
RedBlackNode<AnyType> left;
RedBlackNode<AnyType> right;
int color;
}
private RedBlackNode<AnyType>header;
private RedBlackNode<AnyType>nullNode;
private static final int BLACK=1;
private static final int RED=0;
/**下面这个函数是单旋转用的,item是要比较的AnyType,parent是要旋转子树的父母
* Internal routine that performs a single or double rotation.
* Because the result is attached to the parent,there are four cases.
* Called by handleReorient
* @param item the item in handleReorient
* @param parent the parent of the root of the rotated subtree
* @return the root of the rotated subtree
*/
private RedBlackNode<AnyType> rotate(AnyType item,RedBlackNode<AnyType>parent)
{
if(compare(item,parent)<0)
return parent.left=compare(item,parent.left)<0?
rotateWithLeftChild(parent.left):rotateWithRightChild(parent.left);
else
return parent.right=compare(item,parent.right)<0?
rotateWithLeftChild(parent.right):rotateWithRightChild(parent.right);
}
/*下面是比较函数,如果parent是header,直接返回1,到右边找header的子节点root
*
*/
private final int compare(AnyType item,RedBlackNode<AnyType>t)
{
if(t==header)
return 1;
else
return item.compareTo(t.element);
}
//下面两个和avl的两个单旋转完全一样,只是没了avl的height
private RedBlackNode<AnyType> rotateWithLeftChild(RedBlackNode<AnyType>k2)
{
RedBlackNode<AnyType>k1=k2.left;
k2.left=k1.right;
k1.right=k2;
return k1;
}
private RedBlackNode<AnyType> rotateWithRightChild(RedBlackNode<AnyType>k2)
{
RedBlackNode<AnyType>k1=k2.right;
k2.right=k1.left;
k1.left=k2;
return k1;
}
/*handleReorient用于遇到两个红儿子以及插入树叶时使用
* 要记下current,parent,grand,great
* 一次旋转后grand、great的值将不再正确,
* 不过,肯定到下一次在需要它们的时候它们将被修复
*/
private RedBlackNode<AnyType> current;
private RedBlackNode<AnyType> parent;
private RedBlackNode<AnyType> grand;
private RedBlackNode<AnyType> great;
/**
* Internal routine that is called during an insertion
* if a node has two red children.Perform flip and rotations.
* @param item the item being inserted.
*/
private void handleReorient(AnyType item)
{
//Do the color flip
current.color=RED;
current.left.color=BLACK;
current.right.color=BLACK;
if(parent.color==RED)//Have to rotate
{
grand.color=RED;
//如果item在grand和parent的同侧,单旋转;否则,双旋转(两次旋转)
if((compare(item,grand)<0)!=(compare(item,parent)<0))
parent=rotate(item,grand);//Start dbl rotate
current=rotate(item,great);
current.color=BLACK;
}
header.right.color=BLACK;//Make root black
}
/**
* Insert into the tree
* @param item the item to insert.
*/
public void insert(AnyType item)
{
current=parent=grand=header;
nullNode.element=item;
while(compare(item,current)!=0)
{
great=grand;grand=parent;parent=current;
current=compare(item,current)<0?current.left:current.right;
//Check if two red children;fix if so
if(current.left.color==RED&¤t.right.color==RED)
handleReorient(item);
}
//Insertion fails if already present
if(current!=nullNode)
return;
current=new RedBlackNode<AnyType>(item,nullNode,nullNode);
//Attach to parent
if(compare(item,parent)<0)
parent.left=current;
else
parent.right=current;
handleReorient(item);
}
public void printTree()
{
if(header.right.element==null)
System.out.println("Empty tree");
else
printTree(header.right);
}
//in-order traversal
private void printTree(RedBlackNode<AnyType>t)
{
//注意到在插入完后,每个空指针都会是nullNode,而不是null
//所以我们把到nullNode定为结束
if(t!=nullNode)
{
printTree(t.left);
//if(t.color==RED)加上这一句可以只输出红结点
System.out.print(t.element+" ");
printTree(t.right);
}
}
public static void main(String []args)
{
RedBlackTree<Integer>rbt=new RedBlackTree<Integer>();
rbt.printTree();;
int number[]=new int[] {10,85,15,70,20,60,30,50,65,80,90,40,5,55};
for(int i=0;i<number.length;i++)
rbt.insert(number[i]);
rbt.printTree();
}
}