其他分享
首页 > 其他分享> > 红黑树

红黑树

作者:互联网

1 描述

在之前描述的AVL树中,对于删除某个元素导致树不平衡的情况,需要进行旋转调整,使之恢复平衡。然而,该过程可能需要沿着parent关系经历O(logn)次旋转操作才可使得整棵树平衡。因此,在此基础上设计出来另外一种数据结构--红黑树,它的添加和删除的旋转操作都是O(1)级别,但需要牺牲一些平衡性。
在这里插入图片描述
文章省略了对于B树,AVL树的一些性质和操作的描述,可参考AVL树B树

1.1性质

红黑树需满足以下几条性质:

  1. 结点是RED或者BLACK
  2. 根结点是BLACK
  3. 叶子结点外(这里指外部的空节点)都是BLACK
  4. RED节点的子节点都是BLACK
  5. RED节点的parent都是BLACK
  6. 从根节点到叶子节点的所有路径上不能有2个连续的RED节点
  7. 从任一节点到叶子节点的所有路径都包含相同数目的BLACK节点
  8. 每一个黑节点和红孩子组合,可得到一棵4阶B树,因此可从B树性质的角度分析红黑树,可参考B树

2 操作流程

2.1 查找元素

红黑树继承于二叉排序树,因此其查找方式和二叉排序树相同。另外,对于结点的旋转,获取前驱后继等操作本文不再赘述,可参考前面的章节:AVL树

2.2 添加元素

我们对添加的元素默认设置为RED颜色,有可能破坏红黑树的性质,分为以下几种情况,对其进行相应的调整:

2.3 删除元素

由于红黑继承二叉排序树,因此在删除节点后,如果删除的是度为2的结点,会选择其前驱或者后继来代替被删除的节点,因此最终删除的结点在其转换成B树形式后的叶子节点中,可参考二叉排序树B树

3 算法描述

本节使用Java语言描述红黑树数据结构,由于红黑树继承于平衡二叉搜索树,并在之前的AVL树、平衡二叉搜索树博客中有描述,这里不在赘述。

3.1 节点设计

基于二叉排序树结点,增加color属性。

	private static class RBNode<E> extends Node<E>{
		boolean color = RED;
		public RBNode(E element,Node<E> parent){
			super(element,parent);
		}
	}

3.2 类设计

public class RBTree<E> extends BBSTree<E> {
	private static final boolean RED = false;
	private static final boolean BLACK = true;
	
	public RBTree(){
		this(null);
	}
	
	public RBTree(Comparator<E> comparator){
		super(comparator);
	}
	
	/**
	 * 将节点染成对应的颜色
	 * @param node
	 * @param color
	 * @return
	 */
	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);
	}
	
	/**
	 * 查看某个节点的颜色
	 * @param node
	 * @return
	 */
	private boolean colorOf(Node<E> node){
		//空节点默认为黑色
		return node == null?BLACK:((RBNode<E>)node).color;
	}
	
	/**
	 * 是否为黑色节点
	 * @param node
	 * @return
	 */
	private boolean isBlack(Node<E> node){
		return colorOf(node) == BLACK;
	}
	/**
	 * 是否为红色节点
	 * @param node
	 * @return
	 */
	private boolean isRed(Node<E> node){
		return colorOf(node) == RED;
	}
	
	@Override
	protected Node<E> createNode(E element,Node<E> parent) {
		return new RBNode<>(element,parent);
	}
}

3.3 添加后的调整

添加情况参考2.2。

	@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(isRed(uncle)){		//B树上溢
			black(parent);
			black(uncle);
			red(grand);
			afterAdd(grand);
			return;
		}
		
		//叔父结点不是红色的情况
		if(parent.isLeftChild()){
			if(node.isLeftChild()){	//LL
				black(parent);
				red(grand);
				rotateRight(grand);
			}else{	//LR
				black(node);
				red(grand);
				red(parent);
				rotateLeft(parent);
				rotateRight(grand);
			}
		}else{
			if(node.isRightChild()){		//RR
				black(parent);
				red(grand);
				rotateLeft(grand);
			}else{		//RL
				black(node);
				red(grand);
				red(parent);
				rotateRight(parent);
				rotateLeft(grand);
			}
		}
	}

3.4 删除后的调整

删除情况较为复杂,参考2.3的详细操作流程。

	@Override
	protected void afterRemove(Node<E> node,Node<E> replacement) {
		//被删除的结点只会发生在最后一层,即使不是叶子结点也没关系
		if(isRed(node)){
			return;
		}
		//用于取代的node结点是红色,自身肯定不会是红色,因为不可能Double red
		if(isRed(replacement)){
			black(replacement);
			return;
		}
		//来到这里一定是叶子结点
		//删除的结点是黑色
		Node<E> parent = node.parent;	//parent没有断
		if(parent == null){	//被删除的是根结点
			return;
		}
		boolean left = parent.left == null || node.isLeftChild();	
		//由于在儿叉排序树中会断掉left或者right,则根据left或者right是否为空的情况判定自身为left或right
		Node<E> sibling = left? parent.right : parent.left;
		if(left){	//被删除的结点在左边,兄弟节点在右边,左右操作对称
			if(isRed(sibling)){	//兄弟节点是红色,将其转换成兄弟节点是黑色的情况
				black(sibling);
				red(parent);
				rotateLeft(parent);
				sibling = parent.right;
			}
			//兄弟节点是黑色
			if(isBlack(sibling.left) && isBlack(sibling.right)){	//空节点也是black
				boolean parentBlack = isBlack(parent);
				black(parent);
				red(sibling);
				if(parentBlack){	//父节点为黑,下溢
					afterRemove(parent, null);
				}
			}else{	//兄弟节点至上有一个red
				if(isBlack(sibling.right)){	//左边如果是Black,则为LR,则转换成LL的情况
					rotateRight(sibling);
					sibling = parent.right;
				} 
				color(sibling,colorOf(parent));
				black(parent);
				black(sibling.right);
				rotateLeft(parent);
			}		
		}else{		//被删除结点在右边,兄弟节点在左边
			if(isRed(sibling)){	//兄弟节点是红色,将其转换成兄弟节点是黑色的情况
				black(sibling);
				red(parent);
				rotateRight(parent);
				sibling = parent.left;
			}
			//兄弟节点是黑色
			if(isBlack(sibling.left) && isBlack(sibling.right)){	//空节点也是black
				boolean parentBlack = isBlack(parent);
				black(parent);
				red(sibling);
				if(parentBlack){	//父节点为黑,下溢
					afterRemove(parent, null);
				}
			}else{	//兄弟节点至上有一个red
				if(isBlack(sibling.left)){	//左边如果是Black,则为LR,则转换成LL的情况
					rotateLeft(sibling);
					sibling = parent.left;
				}
				color(sibling,colorOf(parent));
				black(parent);
				black(sibling.left);
				rotateRight(parent);
			}
		}
	}

4 对比AVL树

5 应用场景

标签:node,结点,parent,sibling,BLACK,红黑树,节点
来源: https://www.cnblogs.com/csluoyao/p/13061101.html