《算法导论》中的红黑树
红黑树是二分搜索树中的一种。
满足以下性质:
1.每个节点是红色或者黑色。
2.根节点是黑色。
3.每个叶子节点是黑色。
4.如果一个节点是红色,其孩子节点都是黑色。
5.从任意一个节点到叶子节点经过的黑色节点相同。
对于初学者,直接给出这样的5个性质会难以理解,因此我们先来了解2-3树。
2-3树
红黑树与2-3树是等价的。
满足二分搜索树的基本性质。
节点可以存放一个或者两个元素,如图
2-3树如何维持绝对平衡
2-3树添加节点永远不会添加到一个空的位置,只能和最后找到的叶子节点融合。
对于只有一个节点的2-3树(设这个节点值为42)在添加新节点(设值为37)时首先检查根节点的左子树,若为空则融合两个节点。若继续添加一个值为12的节点,由于新节点不能添加到一个空的位置,所以只能继续和根节点融合,暂时形成一个4节点,如图
红黑树
红黑树与2-3树不同的一点在于红黑树的每个节点只能存一个元素,但是我们可以通过组合的方式表示出2-3树中的3节点。
规定所有的红色节点都是向左倾斜的。
基本数据结构
public class RBTree<K extends Comparable<K>, V> {
private static final boolean RED = true;
private static final boolean BLACK = false;
private class Node{
public K key;
public V value;
public Node left, right;
public boolean color;
public Node(K key, V value){
this.key = key;
this.value = value;
left = null;
right = null;
color = RED;
}
}
private Node root;
private int size;
public RBTree(){
root = null;
size = 0;
}
public int size(){
return size;
}
public boolean isEmpty(){
return size == 0;
}
/**
* 判断节点node的颜色
* @param node
* @return
*/
private boolean isRed(Node node){
if (node == null){
return BLACK;
}
return node.color;
}
}
添加新元素
要始终保持根节点为黑色。
旋转过程:
假设37节点为node,42节点为x
node.right = x.left;
x.left = node;
//对颜色进行维护
x.color = node.color;
node.color = RED;
具体实现
/**
* 左旋转操作
* @param node
* @return
*/
private Node leftRotate(Node node){
Node x = node.right;
/**
* 向左旋转过程
*/
node.right = x.left;
x.left = node;
x.color = node.color;
node.color = RED;
return x;
}
添加元素时最复杂的情况是新加入的元素在原来两个节点的中间,这种情况的操作过程如图所示
完整的添加新元素的逻辑
/**
* 颜色翻转
* @param node
*/
private void flipColors(Node node){
node.color = RED;
node.left.color = BLACK;
node.right.color = BLACK;
}
private Node rightRotate(Node node){
Node x = node.left;
/**
* 向右旋转过程
*/
node.left = x.right;
x.right = node;
x.color = node.color;
node.color = RED;
return x;
}
/**
* 左旋转操作
* @param node
* @return
*/
private Node leftRotate(Node node){
Node x = node.right;
/**
* 向左旋转过程
*/
node.right = x.left;
x.left = node;
x.color = node.color;
node.color = RED;
return x;
}
public void add(K key, V value){
root = add(root, key, value);
root.color = BLACK;
}
private Node add(Node node, K key, V value){
if (node == null){
size++;
return new Node(key, value);
}
if (key.compareTo(node.key) < 0){
node.left = add(node.left, key, value);
}
else if (key.compareTo(node.key) > 0){
node.right = add(node.right, key, value);
}
else {
node.value = value;
}
if (isRed(node.right) && !isRed(node.left)){
node = leftRotate(node);
}
if (isRed(node.left) && isRed(node.left.left)){
node = rightRotate(node);
}
if (isRed(node.left) && isRed(node.right)){
flipColors(node);
}
return node;
}
红黑树的性能总结
对于完全随机的数据,普通的二分搜索树好用。
缺点:极端情况退化成链表或者高度不平衡。
对于查询较多的情况,AVL树好用。
红黑树牺牲了平衡性(2logn高度),统计性能更优(增删改查所有操作)
统计性能更优的树:伸展树SplayTree(原理是刚被访问的节点下次高概率被再次访问)。
红黑树的应用
java.util中的TreeMap和TreeSet都基于红黑树。
Written by Autu
2019.7.5