红黑树的Java实现

本文详细阐述了红黑树的概念、性质以及在Java中的应用实例,通过实例代码展示了红黑树的插入和删除操作,以及如何解决旋转和颜色变更等平衡问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     之前一直在复习数据结构中的树,从最开始的二叉树,到二叉查找树,再到平衡二叉树(AVL),到最后的红黑树。要说复杂度,红黑树确实比之前的AVL树要复杂好多,特别是删除操作。但其实弄懂了红黑树的平衡原理,插入和删除看起来也就没那么复杂了。我主要看的是《算法导论》和《数据结构》这两本书,前者对于红黑树的描述更加准确并且提供了相应的伪代码。而我自己将原理弄懂之后,就对应着伪代码写出了红黑树的Java程序。

     红黑树是一种自平衡二叉查找树,属于平衡二叉树。但它并严格意义上不是平衡二叉树,说它不严格是因为它不是严格控制左、右子树高度或节点数之差小于等于1。但红黑树高度依然是平均log(n),且最坏情况高度不会超过2log(n),这有数学证明。所以它算平衡树,只是不严格。不过严格与否并不影响数据结构的复杂度。

     有了红黑树,AVL树就被放进了博物馆,因为效率的问题,红黑树得到了广泛的应用。Java集合类库中的TreeSet内部就是用的红黑树的数据结构。红黑树的插入操作最多只有两次旋转,删除操作最多三次旋转就能完成。代码如下:

  节点和构造器方法:

  

private static final boolean RED=false;
	private static final boolean BLACK=true;
	private Node<T> root=null;    //根节点
	private Node<T> nullNode=null;  //空节点,所以null节点均指向它
	public RBTree()
	{
		nullNode=new Node<T>(null);
		nullNode.left=nullNode.right=nullNode;
		nullNode.color=BLACK;
	}
//节点类,包含元素和高度信息
private static class Node<T>
{
	private T element;
	private Node<T> parent;
	private Node<T> left;
	private Node<T> right;
	private boolean color;
	public Node(T element)
	{
		this.element=element;
	}
	public Node(T element,Node<T> left,Node<T> right)
	{
		parent=null;
		this.element=element;
		this.left=left;
		this.right=right;
		color=RED;
	}
}

插入方法和修复处理:

//插入方法
public void insert(T t)
{	
	if(root==null | root==nullNode)  //根节点的处理,特殊对待
	{
		root=new Node<T>(t,nullNode,nullNode);
		root.color=BLACK;
		root.parent=nullNode;
		return;
	}
	Node<T> current=root;
	Node<T> parent=current;
	int i=0;
	while(current!=nullNode)
	{
		i=t.compareTo(current.element);
		parent=current;
		if(i>0)
			current=current.right;
		else
			current=current.left;
	}
	if(i!=0)    //说明是新插入的元素,进行插入操作
	{
		if(i>0)    //插入到节点的右子节点上
		{
			parent.right=new Node<T>(t,nullNode,nullNode);
			parent.right.parent=parent;
			current=parent.right;
		}	
		else    //插入到节点的左子节点上
		{
			parent.left=new Node<T>(t,nullNode,nullNode);
			parent.left.parent=parent;
			current=parent.left;
		}
	}
	fixupInsert(current);   //修复操作
}
//插入修复操作,三种情况,最多旋转2次就满足要求
private void fixupInsert(Node<T> node)
{
	if(node==nullNode)
		return;
	Node<T> uncle;
	while(node.parent.color==RED)
	{
		if(node.parent==node.parent.parent.right)
		{
			uncle=node.parent.parent.left;
			if(uncle.color==RED)  //node节点的父节点和叔节点都为红色,则变颜色就行,不用旋转;这种情况才会循环
			{
				uncle.color=node.parent.color=BLACK;
				node.parent.parent.color=RED;
				node=node.parent.parent;
			}
			else     //node节点的叔节点为黑色
			{		 
				if(node==node.parent.left)    //node节点为左节点,要先进行左旋,再进行右旋
				{
					node=node.parent;      //node上移一个节点
					singleLeftRotate(node);   //先左旋
				}
				node.parent.color=BLACK;    //node的父节点颜色涂黑,node的父父节点颜色涂红
				node.parent.parent.color=RED;
				singleRightRotate(node.parent.parent);  //在右旋
			}
		}
		else     //同上面对称
		{
			uncle=node.parent.parent.right;
			if(uncle.color==RED)    //node节点的父节点和叔节点都为红色,则变颜色就行,不用旋转
			{
				uncle.color=node.parent.color=BLACK;
				node.parent.parent.color=RED;
				node=node.parent.parent;
			}
			else
			{		
				if(node==node.parent.right)
				{
					node=node.parent;
					singleRightRotate(node);
				}
				node.parent.color=BLACK;
				node.parent.parent.color=RED;
				singleLeftRotate(node.parent.parent);
			}
		}
	}	
	if(root.parent!=nullNode)   //判断root节点是否有移动,有的话对root重新赋值
		root=root.parent;
		root.color=BLACK;
}

删除操作和修复方法:

public void remove(T t)
{
	if(root==null)
		return;
	Node<T> current=root;
	int i=1;
	while((i=t.compareTo(current.element))!=0& current!=nullNode)
	{
		if(i>0)
			current=current.right;
		else 
			current=current.left;
	}
	if(i==0)
	{
		boolean color=RED;	
		if(current.left!=nullNode & current.right!=nullNode)     //有两个孩子
		{
			Node<T> parent=current;
			current=findMin(current);
				parent.element=current.element;
		}
		Node<T> child;
		if(current.right!=nullNode)     //对child节点进行赋值
		{
			child=current.right;
		}
		else if(current.left!=nullNode)
		{
			child=current.left;
		}
		else
			child=nullNode;
				
		if(current.parent==nullNode)  //真正被删除的节点是根节点,进行特殊处理
		{
			root=child;
			root.color=BLACK;
			return;               //结束方法
		}
		if(current==current.parent.right)
		{
			current.parent.right=child;
			child.parent=current.parent;
		}
		else
		{
			current.parent.left=child;
			child.parent=current.parent;
		}
		color=current.color;
		if(color==BLACK)
			fixupDelete(child);
	}
}
private void fixupDelete(Node<T> node) //删除的修复情况,对应《算法导论》的四种情况
{
	if(node==null)
		return;
	Node<T> w;
	while(node!=root & node.color==BLACK)
	{
		if(node==node.parent.right)
		{
			w=node.parent.left;
			if(w.color==RED)    			//case 1
			{
				w.color=BLACK;			//case 1
				node.parent.color=RED;		//case 1
				singleLeftRotate(node.parent);  //case 1
				w=node.parent.left;		//case 1
		        }
			if(w.right.color==BLACK & w.left.color==BLACK)  //case 2
			{
				w.color=RED; 				//case 2
				node=node.right;			//case 2
			}
			else
			{
				if(w.left.color==BLACK)    //case 3
				{
					w.right.color=BLACK;  //case 3
					w.color=RED;          //case 3
					singleRightRotate(w); //case 3 
					w=w.parent.left;      //case 3
				}
				w.color=node.parent.color;    //case 4
				node.parent.color=BLACK;      //case 4 
				w.left.color=BLACK;           //case 4
				singleLeftRotate(node.parent);//case 4
				node=root;                    //case 4 
			}		
		}
		else
		{
			w=node.parent.right;
			if(w.color==RED)
			{
				w.color=BLACK;
				node.parent.color=RED;
				singleRightRotate(node.parent);
				w=node.parent.right;
			}
			if(w.left.color==BLACK & w.right.color==BLACK)
			{
				w.color=RED;
				node=node.left;
			}
			else
			{
				if(w.right.color==BLACK)
				{
					w.left.color=BLACK;
					w.color=RED;
					singleLeftRotate(w);
					w=w.parent.right;
				}
				w.color=node.parent.color;
				node.parent.color=BLACK;
				w.right.color=BLACK;
				singleRightRotate(node.parent);
				node=root;
			}
		}
	}
	node.color=BLACK;
}

旋转和输出方法:

private void singleRightRotate(Node<T> node)  //右旋方法
{
	Node<T> n=node.right;
	node.right=n.left;
	n.left=node;
	n.parent=node.parent;
	node.parent=n;
	if(node==n.parent.right)
		n.parent.right=n;
	else
		n.parent.left=n;
}
private void singleLeftRotate(Node<T> node)   //左旋方法
{
	Node<T> n=node.left;
	node.left=n.right;
	n.right=node;
	n.parent=node.parent;
	node.parent=n;
	if(node==n.parent.right)    //注意:用node来判断它在parent的左边还是右边,不能用n判断
		n.parent.right=n;
	else
		n.parent.left=n;
}

	//查找右子树中的最小值,用于删除操作
private Node<T> findMin(Node<T> node)	
{
	if(node==null | node==nullNode)
		return null;
	while(node.left!=nullNode)
	{
		node=node.left;
	}
	return node;
}
@Override
public String toString()
{
	StringBuilder sb=new StringBuilder();
	sb.append("[");
	sb.append(print(root));
	sb.append("]");
	return sb.toString();
}
private String print(Node<T> node)  //递归输出,同时输出节点颜色
{
	if(node!=nullNode)
	{
		StringBuilder sb=new StringBuilder();
		sb.append(print(node.left));
		if(node.left!=nullNode)
			sb.append(",");
		sb.append(node.element+" "+((node.color==BLACK)?"黑":"红"));
		if(node.right!=nullNode)
			sb.append(",");
		sb.append(print(node.right));
		return sb.toString();
	}
	return "";
}


结果:





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值