JAVA实现红黑树重构 详解

前言

我们为什么要用红黑树?二叉平衡树不是已经解决了二叉树因为添加和删除导致的退化问题了吗?
是的,二叉平衡树是解决了二叉树的退化问题,但是可否记得,AVL树平衡一次删除可能要做 O(logn)旋转操作,平衡一次添加需要做O(1) 旋转操作。而红黑树平衡一次 添加和 平衡删除都只用做 O(1) 旋转操作。所以我们因为提高平衡效率而使用红黑树。
红黑树有点难,情况太多,但是前面二叉树,BST树,AVL树,还有就是B树(B树不需要实现,但是其性质还有添加删除的处理情况都是必须会的),你都会,那这些繁杂的情况,理解起来也不难。

一,红黑树的性质

  1. 红黑树也是一种自平衡的二叉搜索树
  2. 红黑树必须满足如下五条性质
    (1)节点是 RED 或者 BLACk
    (2)根节点是 BLACK
    (3)叶子节点都是 BLACK
    (4)RED 节点的子节点是 BLACK
    (5)从任一节点到叶子节点的所有路径都包含相同数目的 BLACK 节点。

二,红黑树和B树的等价变换

1.如何变换

在这里插入图片描述

  • 红黑树和 4阶B树有等价性质
  • BLACK 节点和它的 RED 子节点融合在一起,形成一个B树节点
  • 红黑树的 BLACK 节点个数和 4 阶B树的总结点个数相同
    有人会问:你图中的红黑树不满足第三点哎
    并不是的,第三点是叶子节点都是BLACK,比如红色节点17,17节点也有两个叶子节点啊,只不过都为null,我们把那两个为null的节点看做BLACK,只不过图中为标注出null的叶子节点,所以是满足第三点的,在这里我说明一下

2.变换的四种情况

在这里插入图片描述

三,红黑树代码初步构建

现在我们来初步构建红黑树,首先就是颜色属性,还有就是其他一些辅助的方法,比如查看节点颜色的方法,给节点上色的方法,查看兄弟节点的方法,这些都是以后用得着奥的

1.红黑树大体框架

我们要在二叉搜索树节点Node的基础上,添加一个color属性

public class RBtree<E> extends BST<E>{
	
	private static final boolean BLACK = true;
	private static final boolean RED = false;
	
	//构造方法
	public RBtree(){
		this(null);
	}
	
	//带比较器的构造方法
	public RBtree(Comparator<E> comparator) {
		super(comparator);
	}
	
	/*
	 * 红黑树节点
	 */
	private static class RBNode<E> extends Node<E>{
		boolean color=RED;
		
		public RBNode(E element, Node<E> parent) {
			super(element, parent);
			// TODO 自动生成的构造函数存根
		}
	}
}

2.辅助方法

我们在RBTree中加入一些辅助方法,方便以后的操作

(1)染色方法

把结点染成红色或者把结点染成黑色

		/*
		 染色方法
		 */
		private Node<E> color(Node<E> node,boolean color) {
			
			if (node == null)  return node;
			
			((RBNode<E>)node).color = color;
			
			return node;
		}
		
		/*
		 染成红色
		 */
		private Node<E> red(Node<E> node) {
			return color(node, RED);
		}
		
		/*
		 染成黑色
		 */
		private Node<E> black(Node<E> node) {
			return color(node, BLACK);
		}
(2)判断节点颜色的方法

		/*
		 看节点什么颜色
		 */
		
		private boolean colorOf(Node<E> node) {
			return (node == null) ? BLACK : ((RBNode<E>)node).color;
		}
		
		
		/*
		 是否为黑色节点
		 */
		private boolean isBlack(Node<E> node) {
			return colorOf(node) == BLACK;
		}
		
		
		/*
		 是否为红色节点
		 */
		private boolean isRed(Node<E> node) {
			return colorOf(node) == RED;
		}
		
(3)找兄弟结点的方法
		/*
		 查找兄弟节点
		 */
		
		private Node<E> sibling() {
			if (isLeftChild()) {
				return parent.right;
			}
			if (isRightChild()) {
				return parent.left;
			}
			
			return null;
		}

四,红黑树添加方法情况分析

  • 在B树中,新元素必定是添加到叶子节点中。
  • 4阶B树所有节点的元素个数x都满足 1<x<3.
    在这里有个建议:
    我们把新添加的节点都默认为红色,这样做的好处是可以满足红黑树性质中除了第四点以外的其他四个条件。(不一定满足RED节点的子节点都是BLACK这一条)
    在这里插入图片描述
    上图的红黑树囊括了所有红黑树节点的情况,黑,红黑,黑红,红黑红。
    在此红黑树中我们可以把新节点添加到图上的12个位置中,这12中位置可以分为如下4种情况讨论。
1.添加后无需变动的
此情况的判断条件:parent 为 BLACK

在这里插入图片描述
之前我们说过,建议把新添加节点设为RED,有可能会不满足性质4。但是如上的四个位置是满足情况四的。即:
添加后此树完全满足红黑树性质,而且完全满足四阶B树性质。
因此不用做任何的处理。

2.修复性质4,LL\RR情况

剩下的三种情况都是双红情况,即添加节点和其父节点都是红色的
如下图 52 和 60 的添加导致不满足红黑树的性质四。

此情况的判断条件:uncle 节点不是 RED

在这里插入图片描述
因此我们要修复性质四

1.插入节点的parent 染成 BLACK,grand染成 RED。
2.对 grand 单旋转(RR/LL)

如果对旋转了解的可以看我之前的博客

AVL树重构
在这里插入图片描述

3.修复性质4,LR\LR情况
此情况的判断条件:uncle 节点不是 RED

如下图,插入这个两个位置导致的还是不满足性质四。

在这里插入图片描述
因此我们要修复性质四

1.把插入节点自己染成BLACK,其grand节点 染成 RED
2.进行双旋操作
(1)LR,对parent左旋,对grand 右旋
(1)RL,对parent右旋,对grand 左旋
在这里插入图片描述

4.上溢

就只剩下最后这四种情况了,这四种情况都会导致上溢

此情况的判断条件:uncle 节点是 RED

在这里插入图片描述
B树种导致上溢的解决方法是:
假设上溢节点最中间的元素位置为K,将K位置的元素与父节点合并,再将[0,k-1] 和 [k+1,m-1]位置的元素分裂成2个子节点。
17 和 25 都是中间位置,我们取那个呢?在这里取25,因为17 和 33 本来就是25 的左右子树,这样取方便。

我们的操作是:
1.插入节点的parent 和 uncle 节点染成黑色(因为要被分裂成两颗子树,根节点一定要是BLACK)
2.grand向上合并,并且把grand然成红色,当做新插入的节点处理(递归调用)
3.grand 节点向上合并时,可能继续发生上溢,若上溢到根节点,只需把根节点染成BLACK

在这里插入图片描述
图中25,38,55 只是代表一种状态,此时我们还要把25当做新插入的节点。递归调用函数来让 25 节点符合红黑树的要求。

五,红黑树添加方法实现

1.旋转方法

上面讲到了在红黑树中我们平衡二叉树也是需要旋转的操作,而且和AVL树种旋转操作一样(AVL树种旋转方法最后是更新高度,但是红黑树没有高度这个说法,红黑树只用旋转就行)。我们可以提取旋转代码使之公用。但是我这里就不这样做了。我就在粘贴一份。


	/*
	 左旋转
	 */
	private void rotateLeft(Node<E> grand) {
		Node<E> parent = grand.right;
		Node<E> child = parent.left;
		//旋转
		grand.right = child;
		parent.left = grand;
		
		//更新父节点
		//parent
		parent.parent = grand.parent;
		if (grand.isLeftChild()) {
			grand.parent.left = parent;
		}else if (grand.isRightChild()) {
			grand.parent.right = parent;
		}else {
			//grand为根节点
			root = parent;
		}
		//grand
		grand.parent = parent;
		//node 
		if (child != null) {
			child.parent = grand;
		}
	}
	/*
	 右旋转
	 */
	private void rotateRight(Node<E> grand) {
		Node<E> parent = grand.left;
		Node<E> child = parent.right;
		
		grand.left = child;
		parent.right = grand;
		
		parent.parent = grand.parent;
		if (grand.isLeftChild()) {
			grand.parent.left = parent;
		}else if (grand.isRightChild()) {
			grand.parent.right = parent;
		}else {
			root = parent;
		}
		
		grand.parent = parent;
		
		if (child != null) {
			child.parent = grand;
		}	
	}
2. 添加之后的平衡方法

所有情况在之前都已经说过了,只要按照前面分析的四种情况来写代码即可。

	protected void afterAdd(Node<E> node) {
		Node<E> parent = node.parent;
		//新添加的节点可能是根节点
		if (parent == null) {
			black(node);
			return;
		}
		
		//父节点是黑色的四种情况不用处理
		if (isBlack(parent)) return;
		
		//叔父节点
		Node<E> uncle = parent.sibling();
		
		//祖父节点
		Node<E> grand = parent.parent;
		
		//上溢的四种情况
		if (colorOf(uncle) == RED) {
			black(parent);
			black(uncle);
			//把祖父节点当做新添加的节点
			red(grand);
			afterAdd(grand);
			return;
		}else { // 旋转的四种情况
			red(grand);		
			if (parent.isLeftChild()) {    //L
				if (node.isLeftChild()) {  //LL
					black(parent);
				}else {               //LR
					black(node);
					rotateLeft(parent);
				}
				rotateRight(grand);
			}else {                    //R
				if (node.isLeftChild()) {   //RL
					black(node);
					rotateRight(parent);
				}else {                //RR
				    black(parent);
				}
				rotateLeft(grand);
			}	
		}
	
	}
2. 打印红黑树代码

我们制作好了一棵红黑树,怎么看是否真的是一棵红黑树呢?
我们重写了二叉树中的树状打印的方法,在打印时候把颜色属性加上。
在线生成红黑树的网站
然后对比打印的结果和网站上生成的是否一样即可。
树状打印红黑树代码如下

public void toString(Node<E> node, StringBuffer sb, String prefix) {
		if (node == null) {
			return;
		}
		
		toString(node.left,sb,prefix + "L--");
		sb.append(prefix).append(node.element).append("("+colorOf(node)+")").append("\n");
		toString(node.right,sb,prefix + "R--");
	}

main中的方法:
在这里插入图片描述
打印结果如下:
在这里插入图片描述
对比网站上生成的:发现是一样的(true代表黑色,flase代表红色)
在这里插入图片描述

六,红黑树删除方法情况分析

在B树种:最后真正被删除的元素都在叶子节点中。
我们还是分几种情况讨论
我们所用的示例图如下:
在这里插入图片描述

删除红色节点(直接删除)

删除红的节点,这棵树依然满足红黑树 和 4 阶B树的性质。不需要其他的操作。
在这里插入图片描述

删除黑色节点

删除黑色节点分3 中情况讨论,
第一:删除拥有两个red子节点的black节点,这种情况我们无需考虑,还记得BST中删除度为2的节点吗,我们要找到前驱或者后继去取代他(值的覆盖)。而取代之后还是一颗红黑树,无需其他操作。
第二:删除拥有一个red子节点的black节点。
第三:删除的是black叶子节点

1.删除拥有一个red子节点的black节点

还记得我们在BST树种是如何删除度为1的节点的吗?
我们是拿叶子节点来替换他。
此情况的判断条件:用来代替的子节点是red

在这里插入图片描述
我们按照BST树的思路,删除后是这样的:
在这里插入图片描述
出现了红,红的情况。不满足红黑树性质。
我们再把替换好的叶子节点染黑
就可以把它复原成红黑树了
在这里插入图片描述
改一下之前重构的代码
我们说了此种情况的判定条件是,用来代替的节点是红色的。可是我们afterAdd传进来的只有被删除的节点。那还怎么判断呢?因此我们要改一下BST树种的after中的代码,新传入一个参数就是用来代替的节点。

2.删除的是black叶子节点

这种情况十分繁杂,繁杂在什么地方呢?在于下溢出。

先回顾一下m阶B树的性质,假设一个节点存储的元素个数为x
1.根节点中 1 ≤ x ≤ m - 1
2.非跟节点 ⌈m/2⌉ - 1 ≤ x ≤ m - 1

4阶b树是等同于红黑树的,所以非根节点中元素个数 1 ≤ x ≤ 3。
在这里插入图片描述
88节点的删除,使得此节点低于最低元素个数的限制 1 ,导致下溢。

B树种下溢的解决办法:
若临近的兄弟节点至少有⌈m/2⌉个元素(兄弟借的出),可以向其借一个。步骤如下
1.将父节点元素b 发出到下溢节点的 0 位置(最小位置)。
2.用兄弟节点的元素a(最大元素)替代父节点的元素b。
若下溢节点的临近兄弟节点只有 ⌈m/2⌉ - 1 个元素,(兄弟借不出)
1.将父节点的元素b挪下来跟左右子节点合并。
2.若父节点再下溢,重复之前的操作…(套娃)

(1)sibling 为 black

这种情况还要再细分为两种情况。
情况1:sibling 为 black,且sibling至少有一个RED节点(兄弟借的出)
这种情况就是兄弟节点有能力借节点(假设我们删除88节点),这种情况有三种表现形式,如下图。
在这里插入图片描述
我们把88节点删除,有没有发现似曾相识的局面,就是旋转,第一个我们可以使用LR旋转,第二个我们可以使用LL旋转,第三个可以使用LL(最好使用LL,因为第二个也是LL,可以重用代码),也可以使用LR,来使其达到元素平衡。但是颜色要怎么办呢?
在这里插入图片描述

解决此种情况操作如下:(以第一棵树为例)
1.进行旋转操作
2.旋转之后中心节点继承parent 的颜色(即76节点继承80节点的颜色)
3.旋转之后左右子节点染成黑色 (即 72 和80节点变成黑色)

操作后结果如下:
在这里插入图片描述
情况2:sibling 为 black,且sibling没有一个RED节点(兄弟借不出)
此种情况还要分,parent 是red 和parent 是 black两种 处理

在这里插入图片描述

解决此种情况操作如下:
1.将sibling 染成 red,parent 染成black
2.如果parent 是black,就会下溢导致下溢(parent也下溢),我们套娃处理。把88的parent 节点 当做被删除的节点,调用afterRemove(parent,null)

在这里插入图片描述

(2)sibling 为 red

这是最后一种情况了

在这里插入图片描述
我们要删除88,可是88 的兄弟节点是红色节点55。我们删除了88,导致下溢,我们要借节点,可是红色55节点怎么借呢?没法借啊!根节点也没法向下合并啊!合并之后谁能当根节点?
88导致下溢,我们要借节点,只能向76借啊,就是向侄子借。如果们能把侄子变成兄弟节点是不是就变成上面讨论过的情况,sibling是black。

解决此种情况操作如下:
1.sibling染成black,parent染成red,对parent右旋转
2.之后我们就可以套用之前 sibling是black的处理方式了

在这里插入图片描述

七,红黑树删除方法实现

我把需要解释的地方做了注解,在代码下方有解释

protected void afterRemove(Node<E> node, Node<E> replacement) {
		// TODO 自动生成的方法存根
		
		//删除的是红色叶子节点直接删除
		if (isRed(node)) {
			return;
		}
		
		//删除的是黑色节点并且代替的节点是红色
		if (isRed(replacement)) {
			black(replacement);
			return;
		}
		
		Node<E> parent = node.parent;
		
		//删除到最后根节点的情况
		if (parent == null) {
			return;
		}
		//删除的节点是黑色叶子节点
		//注解1:找兄弟节点
		boolean left = parent.left == null || node.isLeftChild();
		
		Node<E> sibling = left ? parent.right:parent.left;
		
		//注解2:镜像情况的讨论
		if (left) { //被删除的节点在左面,兄弟节点在右边
			
			// 复制代码 ,左右调换
			if (isRed(sibling)) {
				black(sibling);
				red(parent);
				rotateLeft(parent);

				sibling = parent.right;
			}
			
			if (isBlack(sibling.right) && isBlack(sibling.left)) { 

				boolean parentIsBlack = isBlack(parent);
				black(parent);
				red(sibling);
				if (parentIsBlack) {
					afterRemove(parent, null);
				}
			}else {                 
				if (isBlack(sibling.right)) {
					rotateRight(sibling);
					sibling = parent.right;
				}
				
				color(sibling, colorOf(parent));
				black(sibling.right);
				black(parent);
				rotateLeft(parent);
			}	
		}else {     //被删除的节点在右面,兄弟节点在左边
			//注解3:处理的先后顺序
			if (isRed(sibling)) {
				black(sibling);
				red(parent);
				rotateRight(parent);
				
				//更换兄弟
				sibling = parent.left;
			}
			
			//兄弟节点是黑色
			
			if (isBlack(sibling.right) && isBlack(sibling.left)) { //兄弟借不出
				//先判断 父节点颜色
				boolean parentIsBlack = isBlack(parent);
				black(parent);
				red(sibling);
				//父节点是black
				if (parentIsBlack) {
					afterRemove(parent, null);
				}
			}else {                 //兄弟可以借出
				
				//注解4
				if (isBlack(sibling.left)) {
					rotateLeft(sibling);
					//为了下面的统一操作,记得更换兄弟
					sibling = parent.left;
				}
				
				color(sibling, colorOf(parent));
				black(sibling.left);
				black(parent);
				rotateRight(parent);
			}	
		}
	}

注解1: 找兄弟节点用 boolean left = parent.left == null || node.isLeftChild();
为什么不用sibling?因为删除节点node(假设是左孩子)传入到afterremove的时候,node 和 他parent 的关系已经断了(parent.left = = null),所以用sibling函数根本找不到。所以我们换一种思路如果 node.parent.left = = null,那肯定是删除了left的叶子节点啊。我们就用这种方式来判断(注意前提条件,删除叶子节点)。至于后面还连上 node.isLeftChild() 那是为了防止黑色节点下溢,需要再次调用afterRemove(node,null)。此时删除的就不一定是叶子节点了,不能用看null的方式判断。这种情况有个好处就是node 和 parent 的关系是没断的,我们只是当做删除node 后做平衡的处理,所以我们用node.isLeftChild();判断。


注解2:我们上面分析的情况,都是删除节点在右,兄弟节点在左的。还有一种情况是删除节点在左,兄弟节点在右的,我们只需在删除节点在右,兄弟节点在左的情况下,把左换成右,把又换成左就行。


注解3:处理顺序问题,还记得我们之前处理删除度为2的节点是放置在最前操作的。为什么呢?删除度为2的节点,首先我们要用后继节点替换原来节点,再删除后继节点,而后继节点一定度为1或者0,正好适用下面删出度为1或0 的情况。
本次的处理顺序和上面的类似。因为兄弟节点若是红色的,我们通过染色,和旋转,变成下面将要处理的情况。所以也放在最前面讨论。


注解4:这种情况下还有三种情况,如下图
在这里插入图片描述
第一个需要 染色 + 对parent 左旋转 + 对grand 右旋转
第二个需要 染色 + 对grand 右旋转
第三个需要 染色 + 对grand 右旋转
我们发现 只要将第一种情况的对parent左旋转先处理(注意要及时的变sibling),之后的操作基本是一致的。我们的代码也是沿用这种思路。

八,如何理解红黑树的平衡

我们已经把红黑树的代码重构完成了,我们都是按照红黑树的性质来做的,为什么红黑树的性质能保证一棵树不退化成链表呢?
那五条性质是保证红黑树是一颗4阶B树,为什么要保证他是4阶B树呢?
因为四阶B树矮啊,B树本身就是十分平衡的。

在这里插入图片描述

看上图红黑树,如果用AVL树的观点来看,55节点还是失衡了,这个树还是没到十分的平衡。
因为红黑树平衡的标准就是相对宽松的(没有一条路径长度会大于其他路径长度的2倍),只要不退化就成,没有AVL树那么严格。红黑树可以理解为一种弱平衡,或者黑高度平衡(从任一节点到跟节点的所有路径都包含相同数目的 BLACK 节点。)

九,AVL vs 红黑树

在这里插入图片描述

十,完整代码

public class RBtree<E> extends BST<E>{
	
	private static final boolean BLACK = true;
	private static final boolean RED = false;
	
	//构造方法
	public RBtree(){
		this(null);
	}
	
	//带比较器的构造方法
	public RBtree(Comparator<E> comparator) {
		super(comparator);
	}
	
	/*
	 * 红黑树节点
	 */
	private static class RBNode<E> extends Node<E>{
		boolean color = RED;
		
		public RBNode(E element, Node<E> parent) {
			super(element, parent);
			// TODO 自动生成的构造函数存根
		}
		
		@Override
		public String toString() {
			String str = " ";
			if (color == RED) {
				str = "红色_" ;
			}
			if (color == BLACK) {
				str = "黑色_" ;
			}
			return str + element.toString();
		}
	}
	
	/*
	 染色方法
	 */
	private Node<E> color(Node<E> node,boolean color) {
		
		if (node == null)  return node;
		
		((RBNode<E>)node).color = color;
		
		return node;
	}
	
	/*
	 染成红色
	 */
	private Node<E> red(Node<E> node) {
		return color(node, RED);
	}
	
	/*
	 染成黑色
	 */
	private Node<E> black(Node<E> node) {
		return color(node, BLACK);
	}
	
	/*
	 看节点什么颜色
	 */
	
	private boolean colorOf(Node<E> node) {
		return (node == null) ? BLACK : ((RBNode<E>)node).color;
	}
	
	
	/*
	 是否为黑色节点
	 */
	private boolean isBlack(Node<E> node) {
		return colorOf(node) == BLACK;
	}
	
	
	/*
	 是否为红色节点
	 */
	private boolean isRed(Node<E> node) {
		return colorOf(node) == RED;
	}
	

	@Override
	protected void afterAdd(Node<E> node) {
		Node<E> parent = node.parent;
		//新添加的节点可能是根节点
		if (parent == null) {
			black(node);
			return;
		}
		
		//父节点是黑色的四种情况不用处理
		if (isBlack(parent)) return;
		
		//叔父节点
		Node<E> uncle = parent.sibling();
		
		//祖父节点
		Node<E> grand = parent.parent;
		
		//上溢的四种情况
		if (colorOf(uncle) == RED) {
			black(parent);
			black(uncle);
			//把祖父节点当做新添加的节点
			red(grand);
			afterAdd(grand);
			return;
		}else { // 旋转的四种情况
			red(grand);		
			if (parent.isLeftChild()) {    //L
				if (node.isLeftChild()) {  //LL
					black(parent);
				}else {               //LR
					black(node);
					rotateLeft(parent);
				}
				rotateRight(grand);
			}else {                    //R
				if (node.isLeftChild()) {   //RL
					black(node);
					rotateRight(parent);
				}else {                //RR
				    black(parent);
				}
				rotateLeft(grand);
			}	
		}
	
	}
	
	/*
	 左旋转
	 */
	private void rotateLeft(Node<E> grand) {
		Node<E> parent = grand.right;
		Node<E> child = parent.left;
		//旋转
		grand.right = child;
		parent.left = grand;
		
		//更新父节点
		//parent
		parent.parent = grand.parent;
		if (grand.isLeftChild()) {
			grand.parent.left = parent;
		}else if (grand.isRightChild()) {
			grand.parent.right = parent;
		}else {
			//grand为根节点
			root = parent;
		}
		//grand
		grand.parent = parent;
		//node 
		if (child != null) {
			child.parent = grand;
		}
	}
	/*
	 右旋转
	 */
	private void rotateRight(Node<E> grand) {
		Node<E> parent = grand.left;
		Node<E> child = parent.right;
		
		grand.left = child;
		parent.right = grand;
		
		parent.parent = grand.parent;
		if (grand.isLeftChild()) {
			grand.parent.left = parent;
		}else if (grand.isRightChild()) {
			grand.parent.right = parent;
		}else {
			root = parent;
		}
		
		grand.parent = parent;
		
		if (child != null) {
			child.parent = grand;
		}	
	}
	
	
	
	@Override
	protected void afterRemove(Node<E> node, Node<E> replacement) {
		// TODO 自动生成的方法存根
		
		//删除的是红色叶子节点直接删除
		if (isRed(node)) {
			return;
		}
		
		//删除的是黑色节点并且代替的节点是红色
		if (isRed(replacement)) {
			black(replacement);
			return;
		}
		
		Node<E> parent = node.parent;
		
		//删除到最后根节点的情况
		if (parent == null) {
			return;
		}
		//删除的节点是黑色叶子节点
		//注解1:找兄弟节点
		boolean left = parent.left == null || node.isLeftChild();
		
		Node<E> sibling = left ? parent.right:parent.left;
		
		//注解2:镜像情况的讨论
		if (left) { //被删除的节点在左面,兄弟节点在右边
			
			// 复制代码 ,左右调换
			if (isRed(sibling)) {
				black(sibling);
				red(parent);
				rotateLeft(parent);

				sibling = parent.right;
			}
			
			if (isBlack(sibling.right) && isBlack(sibling.left)) { 

				boolean parentIsBlack = isBlack(parent);
				black(parent);
				red(sibling);
				if (parentIsBlack) {
					afterRemove(parent, null);
				}
			}else {                 
				if (isBlack(sibling.right)) {
					rotateRight(sibling);
					sibling = parent.right;
				}
				
				color(sibling, colorOf(parent));
				black(sibling.right);
				black(parent);
				rotateLeft(parent);
			}	
		}else {     //被删除的节点在右面,兄弟节点在左边
			//注解3:处理的先后顺序
			if (isRed(sibling)) {
				black(sibling);
				red(parent);
				rotateRight(parent);
				
				//更换兄弟
				sibling = parent.left;
			}
			
			//兄弟节点是黑色
			
			if (isBlack(sibling.right) && isBlack(sibling.left)) { //兄弟借不出
				//先判断 父节点颜色
				boolean parentIsBlack = isBlack(parent);
				black(parent);
				red(sibling);
				//父节点是black
				if (parentIsBlack) {
					afterRemove(parent, null);
				}
			}else {                 //兄弟可以借出
				
				//注解4
				if (isBlack(sibling.left)) {
					rotateLeft(sibling);
					//为了下面的统一操作,记得更换兄弟
					sibling = parent.left;
				}
				
				color(sibling, colorOf(parent));
				black(sibling.left);
				black(parent);
				rotateRight(parent);
			}	
		}
	}
	

	
	@Override
	protected Node<E> createNode(E element, Node<E> parent) {
		return new RBNode<E>(element, parent);
	}
	
	/*
	 * 树状打印红黑树
	 */
	@Override
	public void toString(Node<E> node, StringBuffer sb, String prefix) {
		if (node == null) {
			return;
		}
		
		toString(node.left,sb,prefix + "L--");
		sb.append(prefix).append(node.element).append("("+colorOf(node)+")").append("\n");
		toString(node.right,sb,prefix + "R--");
	}
	
	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值