红黑树之Java实现

Java中的TreeMap底层是由红黑树实现的

红黑树是具有下列着色性质的二叉树

  1. 每一个节点或者着红色,或者着黑色
  2. 根是黑色的
  3. 每个叶子都是黑色的空节点
  4. 每个红色节点的两个子节点都是黑色的
  5. 从一个节点到一个null引用的每一条路径必须包含相同数目的黑色节点

将新项作为树叶插入树中,该项必须为红色

  • 如果其父节点是黑色,那么插入完成
  • 如果其父节点是红色,那么需要进行颜色的改变和树的旋转
    此时其祖父一定是黑色的
    若是一字型的情形,那么将父与祖父进行单旋转,此时祖父-父-子的颜色分别为黑-红-黑
    若是之字形的情形,那么进行双旋转:子与父进行单旋转,再与祖父进行单旋转,此时祖父-父-子的颜色分别为黑-红-黑

黑节点的孩子可以是黑节点或红节点,一旦有红节点之后,红节点所在的子树必然是红- 黑-红…交错的

class RedBlackTree<T extends Comparable<? super T>>
{
	//设定颜色
	public static final int BLACK = 1;
	public static final int RED = 0;
	
	//节点类
	private static class RedBlackNode<T>
	{
		T element;
		RedBlackNode<T> left;
		RedBlackNode<T> right;
		int color;
		
		RedBlackNode(T element)
		{
			this(element, null, null);
		}
		
		RedBlackNode(T element, RedBlackNode<T> left, RedBlackNode<T> right)
		{
			this.element = element;
			this.left = left;
			this.right = right;
			this.color = RedBlackTree.BLACK;
		}
	}
	
	//声明头节点,其右子树是根
	private RedBlackNode<T> header;
	
	//声明一个空节点
	private RedBlackNode<T> nullNode;
	
	//初始化红黑树
	public RedBlackTree()
	{
		nullNode = new RedBlackNode<>(null);
		nullNode.left = nullNode.right = nullNode;
		
		header = new RedBlackNode<>(null);
		header.left = header.right = nullNode;
	}
	
	//判断树是否为空
	public boolean isEmpty()
	{
		return header.right == nullNode;
	}
	
	//清空树
	public void makeEmpty()
	{
		header.right = nullNode;
	}
	
	
	//插入方法的辅助节点
	private RedBlackNode<T> current;
	private RedBlackNode<T> parent;
	private RedBlackNode<T> grand;
	private RedBlackNode<T> great;  //曾祖
	
	//插入
	public void insert(T t)
	{
		//初始化子、父、祖父
		current = parent = grand = header;
		
		//为空节点赋值,空节点应该在所有叶子位置上
		nullNode.element = t;
		
		//自顶向下找到current要插入的位置
		while(compare(t, current) != 0)//找到空节点的时候相等 
		{
			//向下一层
			great = grand;
			grand = parent;
			parent = current;
			
			//判断向下的方向(左、右)
			if( compare(t, current) < 0)
				current = current.left;
			else
				current = current.right;
			
			//如果current的两个子树都是红色,那么重新处理
			if(current.left.color == RED && current.right.color == RED)
				handleReorient(t);
		}
		
		//不允许重复元素
		if(current != nullNode)  
			return;
		current = new RedBlackNode<>(t, nullNode, nullNode);//不是null,下面要用到它的子树
		
		//与父节点相连
		if(compare(t, parent) < 0)
			parent.left = current;
		else
			parent.right = current;
		handleReorient(t);
		
		
	}
	
	private void handleReorient(T t)
	{
		//颜色翻转,current变为红,子树变为黑
		current.color = RED;
		current.left.color = BLACK;
		current.right.color = BLACK;
		
		//当parent为黑,那么就颜色翻转后符合规则,继续向下
		//当parent为红,current必须为黑,因此需要旋转
		if(parent.color == RED) 
		{
			//parent为红,grand一定为黑,需要变红
			grand.color = RED;
			
			if((compare(t, grand) < 0) != (compare(t, parent) < 0))  //之字形,两次旋转
				parent = rotate(t, grand);   //父与祖进行旋转
			
			current = rotate(t, great); //一字型的旋转和之字形的双旋转第二次,当前与曾祖进行旋转 
			
			//current必须为黑
			current.color = BLACK;
			
		}
		//根一定为黑色
		header.right.color = BLACK;
		
	}
	
	//旋转方法
	private RedBlackNode<T> rotate(T t, RedBlackNode<T> parent)
	{
		if(compare(t, parent) < 0)
				return parent.left = (compare(t, parent.left) < 0)?
						rotateWithLeftChild(parent.left):  //t在parent.lef左下方,左一字型
						rotateWithRightChild(parent.left);//t在parent.right右下方,之字形双旋转第一次

		else
				return parent.right =(compare(t, parent.right) < 0)? 
						rotateWithLeftChild(parent.right)://t在parent.right左下方,之字形双旋转第一次
						rotateWithRightChild(parent.right);//t在parent.right右下方,右一字型

	}
	
	//左左单旋转
	private RedBlackNode<T> rotateWithLeftChild(RedBlackNode<T> k2)
	{
		RedBlackNode<T> k1 = k2.left;
		k2.left = k1.right;
		k1.right = k2;
			
		return k1;
			
	}
		
	//右右单旋转
	private RedBlackNode<T> rotateWithRightChild(RedBlackNode<T> k1)
	{
		RedBlackNode<T> k2 = k1.right;
		k1.right = k2.left;
		k2.left = k1;
			
		return k2;
			
	}
	
	//比较方法,考虑p是头节点(元素为null),那么t永远大于头节点的元素
	private int compare(T t, RedBlackNode<T> p)
	{
		if(p == header)
			return 1;
		
		return t.compareTo(p.element);
	}
	

	
	//最小值
	public T findMin()
	{
		if(isEmpty())
			throw new RuntimeException();
		
		RedBlackNode<T> p = header.right;
		while(p.left != nullNode)
			p = p.left;
		
		return p.element;
	}
	
	//最大值
	public T findMax()
	{
		if(isEmpty())
			throw new RuntimeException();
		
		RedBlackNode<T> p = header.right;
		
		while(p.right != nullNode)
			p = p.right;
		
		return p.element;
	}
	
	//包含
	public boolean contains(T t)
	{
		if(isEmpty())
			return false;
		
		nullNode.element = t;
		current = header.right;
		
		while(true)
		{
			if(t.compareTo(current.element) < 0)
				current = current.left;
			else if(t.compareTo(current.element) > 0)
				current = current.left;
			else if(current != nullNode)
				return true;
			else
				return false;
		}
	}
	
	
	//中序遍历
	public void midPrintTree()
	{
		if(isEmpty())
			System.out.println("Empty Tree");
		
		midPrintTree(header.right);
	}
	private void midPrintTree(RedBlackNode<T> p)
	{
		if(p != nullNode)
		{
			midPrintTree(p.left);
			System.out.println(p.element +" "+ p.color);
			midPrintTree(p.right);
		}
	}
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值