红黑树的创建和插入

代码来自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引用
     */
    //

     12-10

12-11

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&&current.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();
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_41677899/article/details/84921441