红黑树 自顶向下插入操作(一)

本文介绍红黑树自顶向下的插入操作,重点讲解了处理违反红黑树性质的过程,包括通过调整节点颜色及单双旋转来维持树的平衡。

1. 红黑树简介:

   对于红黑树 一种变异型的平衡二叉树,保持最坏的情况下查找时间的复杂度o(logN),在 红黑树的 插入操作过程中有两种 方式 自顶向下插入 和 自低向上的插入;相比较而言 各自有其优点,不管 采用何种方式 在 Insert 操作中 规定:将插入结点染成红色主要是满足 红黑树 性质5(从一个节点到NULL任一路径上有相同的黑色结点数)。

2. 自顶向下插入操作:每一次插入从Heade遍历开始,让插入点当成叶子节点进行插入

 根据数据结构与算法分析Java,在自低向上插入过程中为了满足性质5 在树的结构体中 加入parent的父节点指针保留父链保存路径,同时在 调整树结构过程同时 需要更新父指针的指向,特别是在删除采用自低向上操作方式非常的复杂,如果采用自顶向下过程 向下遍历 树,不同于自低向上算法,这里 自顶向下过程中 发现当前节点有两个RED节点时候,进行处理,处理方式当前节点设置RED,两个儿子设置成BLACK,如果 当前节点与其父节点都是RED(违背性质2不能连续出现两个RED),必须进行 单旋(当前节点parent与祖父结点)或者双旋(当前节点与祖父结点进行双旋)--类似与AVL的旋转。

3. 自顶向下插入 步骤:

    1). 新插入的结点 每一次从根结点起进行遍历current ,parent,gparent,great,分别表示 当前节点,当前节点父节点,其祖父结点,其曾祖父结点;

    2) 如果 current 结点 有两个RED 儿子,那么就将两个RED 孩子 设置成BLACK,current 设置成RED,同时比较current 结点 与parent 结点的颜色 都是RED 执行步骤3 否则直接执行步骤4;

   3). 目的处理 current 结点与 parent 结点都是RED 情形,那么其兄弟结点S必定是BLACk,因为从上向下遍历时候已经处理 儿子全部是RED 情况 对于这种 情形 出现两种子情况;(只进行左侧讨论,右侧讨论镜像)

       case 1 . current.color=RED,parent.color=RED, current=parent.left , 形成是一字型,对parent与gparent单旋转 ,将gparent 变成parent的右孩子。 

        case 2. current.color=RED,parent.color=RED, current=parent.right. , 形成是之字型,需要进行双旋将 current 移动到 祖父结点上 --AVL 双旋可以转化成单旋 singleRightroate(gparent.left) 在之后singLeftRoate(greate.left)

最后记得 更新current= 旋转之后的结点 明显发现旋转之后之前 gparent设置成 RED


   

 4). 完成 2(3)之后 ,继续往下前进遍历,重复2,4 过程,直到到达KEy或者 nullpoint点,至此current=null 从而进行


4. 自顶向下编程实现:

     1). 实现 小技巧: ① 建立NULLNode 代表NULL,如果一个节点指向nullNode代表其指向NULl结点

                                     ② 根节点Heade不是真正的 root,Heade.right 才是真正的根节点,这样实现好处开始时候并不清楚,直到实现 自顶向下删除操作时候发现其妙处。

  2). 接下来 介绍 处理两个red 孩子的 程序段(步骤3):

    这里 程序中判断做 双旋还是单旋 小技巧 :判断 current 是之字形还是一字形:item(要插入结点)  与祖父结点比较结果 和item 与parent 比较结果 一致性,如果是一字形 item 在 parent的左子树中同时 item在祖父结点的左子树中, 如果之字型:item 在parent 在右子树中,在gparent的左子树中(镜像相反),综合结果 两者的比较结果一定不是相同 的

    (compare(item,gparent)<0)  == (compare(item,parent)<0) 

     

/**
	 * 从顶向下 遍历 时候 current 结点 有 两个红色 儿子 --- 目的 保证 current的兄弟 结点 永远是黑色 不同于自低向上删除
	 * 设置 让 红色儿子成为 BLACk,当前结点 变成 RED 同时 与 其父节点 进行颜色 比较 
	 * parent 是 RED 必须 进行旋转(旋转中判断 是进行 单旋 还是 双旋)
	 * */
	private void handReorient(T item) {
		
		current.color=RED;
		current.left.color=BLACK;
		current.right.color=BLACK;
		
		//发现 current 与父节点 都是 
		if(parent.color==RED)
		{
			gparent.color=RED;
			
			/**判断 是对 current 的 父结点 还是祖父结点 进行 旋转
			 * 插入 结点 比当前节点 父节点 小 一字型 旋转 祖父结点---单旋转 对 祖父结点
			 * 插入结点 比 当前 结点 父节点 大 之字型---双旋转 --先对 父节点 在对 祖父结点 */
			if((compare(item, gparent)<0)!=(compare(item, parent)<0))
			{
				parent=roate(item,gparent);// 祖父 --的 儿子 (当前节点的 父节点 之字形) 运行之后还会//运行 下一行程序 相当于双旋
			}
			current=roate(item,great);// 旋转曾祖父的儿子 只进行单一 单旋
			
			current.color=BLACK;// 调整 树结构 重新遍历 新插入点
					
					
		}
		heade.right.color=BLACK;// make the root BLACk
	}

Roate 函数: 借助 AVL中 roateWithLeft 、roateWithRight:

                  翻转的 输入结点的儿子结点 ,因此为什么要其曾祖结点 great

/**
	 *  roate 旋转 只是 输入结点的 儿子结点 ,更新 树的结构
	 *  */
	private RedBlackNode<T> roate(T item, RedBlackNode<T> parent) {
		if(compare(item, parent)<0)
		{
			return parent.left=compare(item, parent.left)<0?
					rotateWithLeftChild(parent.left):  // LL
				   rotateWithRightChild(parent.left)	;//LR
			
		}
		else
            return parent.right = compare( item, parent.right ) < 0 ?
                rotateWithLeftChild( parent.right ) :  // RL
                rotateWithRightChild( parent.right );  // RR
  
	}
	

package com.Tree;

import java.util.LinkedList;
import java.util.Queue;

public class RedBlackTree<T extends Comparable<? super T>> {

	private static final boolean RED = true;
	private static final boolean BLACK = false;
	
	public RedBlackNode<T> heade;
	public RedBlackNode<T> nullNode;
	
	private RedBlackNode<T>current;
	private RedBlackNode<T> parent;
	private RedBlackNode<T>gparent;
	private RedBlackNode<T> great;
	public RedBlackTree()
	{
		heade=new RedBlackNode<T>(null);
		nullNode=new RedBlackNode<T>(null);
		nullNode.left=nullNode.right=nullNode;
		heade.left=heade.right=nullNode;
	}
	private static class RedBlackNode<T>{
		
		T element;
		RedBlackNode<T> left;
		RedBlackNode<T> right;
		boolean color;
		
		public RedBlackNode(T e)
		{
			this(e,BLACK,null,null);
		}

		public RedBlackNode(T e, boolean c, RedBlackNode<T> object, RedBlackNode<T>object2) {
			this.element=e;
			
			this.color=c;
			
			this.left=object;
			
			this.right=object2;
		}
	}
	
	
	/**
	 * Insert into the tree 
	 *    自顶向下的插入方法
	 * */
	
	public void insert(T item)
	{
		insert(item,heade);
	}
	
	/**
	 * s输出 结点 层次遍历*/
	public void printTree(RedBlackNode<T> heade)
	{
		DoPrint(heade.right,0);
	}
	// 利用 层次遍历
	private void DoPrint(RedBlackNode<T> r,int depth)
	{
		if(r!=nullNode)
		{
			DoPrint(r.left, depth+1);
			for(int i=0;i<depth;i++)
				System.out.print("     ");
			System.out.println(r.element+(r.color==RED?"RED":"BLACK"));
			DoPrint(r.right,depth+1);
		}
	}
	public static void main(String[] args)
	{
		int [] array={10,85,15,70,20,60,30,50,65,80,90,40,5,55};
		RedBlackTree<Integer> redBlackTree=new RedBlackTree<>();
		for(int i=0;i<array.length;i++)
			redBlackTree.insert(array[i]);
		
		redBlackTree.printTree(redBlackTree.heade);
	}
	public void LevelPrint(RedBlackNode<T> head)
	{
		RedBlackNode<T> root=head.right;
		
		Queue<RedBlackNode<T>> queue=new LinkedList<RedBlackTree.RedBlackNode<T>>();
		
		queue.add(root);
		
	}
	private void insert(T item, RedBlackNode<T> node) 
	{
		// 自顶向下 的 插入 边 查找 一边进行 树的调整
		current=parent=gparent=heade;
		nullNode.element=item;
		while(compare(item,current)!=0)
		
		{
			/*顶 往下 遍历 定位到 其 插入位置**/
			great=gparent;gparent=parent;parent=current;
			
			 current=compare(item, current)<0? current.left:current.right;
			 
			 /**左右 儿子 是 RED 自顶向下的 插入过程中 主要遇到情形--目的 让兄弟结点 U 永远是 黑色*/
			 if(current.left.color==RED && current.right.color==RED)
				 handReorient(item);
		}
		
		// Insert fails if already present
		if(current!=nullNode)
			return ;
		// 遍历 直接 遍历到其叶子节点上
		current=new RedBlackNode<T>(item,BLACK,nullNode,nullNode);
		
		if(compare(item, parent)<0)
			parent.left=current;
		else
			parent.right=current;
		handReorient(item);
	}

	/**
	 * 从顶向下 遍历 时候 current 结点 有 两个红色 儿子 --- 目的 保证 current的兄弟 结点 永远是黑色 不同于自低向上删除
	 * 设置 让 红色儿子成为 BLACk,当前结点 变成 RED 同时 与 其父节点 进行颜色 比较 
	 * parent 是 RED 必须 进行旋转(旋转中判断 是进行 单旋 还是 双旋)
	 * */
	private void handReorient(T item) {
		
		current.color=RED;
		current.left.color=BLACK;
		current.right.color=BLACK;
		
		//发现 current 与父节点 都是 
		if(parent.color==RED)
		{
			gparent.color=RED;
			
			/**判断 是对 current 的 父结点 还是祖父结点 进行 旋转
			 * 插入 结点 比当前节点 父节点 小 一字型 旋转 祖父结点---单旋转 对 祖父结点
			 * 插入结点 比 当前 结点 父节点 大 之字型---双旋转 --先对 父节点 在对 祖父结点 */
			if((compare(item, gparent)<0)!=(compare(item, parent)<0))
			{
				parent=roate(item,gparent);// 祖父 --的 儿子 (当前节点的 父节点 之字形) 运行之后还会运行 下一行程序 相当于双旋
			}
			current=roate(item,great);// 旋转曾祖父的儿子 只进行单一 单旋
			
			current.color=BLACK;// 调整 树结构 重新遍历 新插入点
					
					
		}
		heade.right.color=BLACK;
	}

	/**
	 *  roate 旋转 只是 输入结点的 儿子结点 ,更新 树的结构
	 *  */
	private RedBlackNode<T> roate(T item, RedBlackNode<T> parent) {
		if(compare(item, parent)<0)
		{
			return parent.left=compare(item, parent.left)<0?
					rotateWithLeftChild(parent.left):  // LL
				   rotateWithRightChild(parent.left)	;//LR
			
		}
		else
            return parent.right = compare( item, parent.right ) < 0 ?
                rotateWithLeftChild( parent.right ) :  // RL
                rotateWithRightChild( parent.right );  // RR
  
	}
	
	/**
     * Rotate binary tree node with left child.
     */
    private RedBlackNode<T> rotateWithLeftChild( RedBlackNode<T> k2 )
    {
        RedBlackNode<T> k1 = k2.left;
        k2.left = k1.right;
        k1.right = k2;
        return k1;
    }

    /**
     * Rotate binary tree node with right child.
     */
    private RedBlackNode<T> rotateWithRightChild( RedBlackNode<T> k1 )
    {
        RedBlackNode<T> k2 = k1.right;
        k1.right = k2.left;
        k2.left = k1;
        return k2;
    }

	private int compare(T item, RedBlackNode<T> current) {
		if (current==heade)
			return 1;
		else
			return item.compareTo(current.element);
		
	}
public void LevelPrint(RedBlackNode<T> head)
	{
		RedBlackNode<T> root=head.right;
		
		Queue<RedBlackNode<T>> queue=new LinkedList<RedBlackTree.RedBlackNode<T>>();
		
		queue.add(root);
		RedBlackNode<T> current;
		while(!queue.isEmpty())
		{		
			/**按层次输出*/
			int L=queue.size();// 记录上一层 结点数
			for(int i=0;i<L;i++)
			{
				current=queue.poll();
				System.out.print(current.element +(current.color==RED?"RED":"BLACK")+"   ");
				if(current.left!=nullNode) queue.add(current.left);
					
				if(current.right!=nullNode ) queue.add(current.right);
					
				
			}
			System.out.println();
		}
		
	}
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值