说起红黑树的性质,一般引用《算法导论》中对红黑树的五条性质定义
初次接触这五条性质时,一头雾水,虽然背下来不难,但是不知道为什么要这样定义。
换个角度,从二三树出发,可以真正理解这五个性质。
二三树是一种绝对平衡树,即所有的叶子节点都在同一层。
二三树的节点可以放置一或二个元素,同时具有二分搜索的特性,当节点有一个元素时,节点可以有左右两个子节点,称为二节点,左子节点<当前节点<右子节点。当节点有两个元素时,则有左中右三个子节点,称为三节点,左子节点<当前节点左元素<中子节点<当前节点右元素<右子节点。因为由二节点和三节点所组成,所以称为二三树。
我们可以这样表示二三树的节点,一个二节点由一个黑色节点表示,一个三节点由一个红色节点和一个黑色节点左右并列表示,我们始终将红色节点放在左边。这时候就可以发现,二三树可以等价转换为红黑树。
此时我们重新理解那五条性质
红黑树的java实现可以参考TreeMap。
// 单个Node的结构
static final class Entry implements Map.Entry {
K key;
V value;
Entry left;
Entry right;
Entry parent;
boolean color = BLACK;
/**
* Make a new cell with given key, value, and parent, and with
* {@code null} child links, and BLACK color.
*/
Entry(K key, V value, Entry parent) {
this.key = key;
this.value = value;
this.parent = parent;
}
}
向红黑树中添加一个元素与二叉树类似,根据左大右小的规则,通过递归或循环找到放入元素的位置。关键在于放入后,使树继续满足红黑树的性质。这里看看TreeMap的做法。
private void fixAfterInsertion(Entry x) {
// 首先将新节点x设为红色
x.color = RED;
// 如果x节点不为空且不为根节点且父节点是红色,走平衡逻辑
while (x != null && x != root && x.parent.color == RED) {
// 如果x的父节点是爷爷的左子
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
// y为x的右叔叔
Entry y = rightOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
// 如果右叔叔y是红色
// 则p黑,y黑,g红(即颜色翻转)
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
// 将x设为爷爷节点,从爷爷节点开始重新走平衡逻辑
x = parentOf(parentOf(x));
} else {
// 如果右叔叔y是黑色,若y为普通节点,则左右路径黑色节点数不等,所以y为NIL
if (x == rightOf(parentOf(x))) {
// 如果x是右孩子,即
// g黑
// /
// p红
// \
// x红
x = parentOf(x);
// 对p进行右旋,形成
// g黑 g黑
// / 又因为x=parentOf(x) /
// x红 ,所以x还是指向最低层 p红
// / ------------------》 /
// p红 x红
rotateLeft(x);
}
// 将p染黑,g染红
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
// 对g进行右旋,形成
// p黑
// / \
// x红 g红
rotateRight(parentOf(parentOf(x)));
}
} else {
// 否则,x的父节点是爷爷的右子
// 则y为x的左叔叔
Entry y = leftOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
// 如果y红
// 则p黑,y黑,g红(即颜色翻转)
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
// 将x设为爷爷节点,从爷爷节点开始重新走平衡逻辑
x = parentOf(parentOf(x));
} else {
// 否则y为NIL
if (x == leftOf(parentOf(x))) {
// 如果x是左子,即
// g黑
// \
// p红
// /
// x红
x = parentOf(x);
rotateRight(x);
// 形成
// g黑
// \
// p红
// \
// x红
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateLeft(parentOf(parentOf(x)));
// 形成
// p黑
// / \
// g红 x红
}
}
}
root.color = BLACK;
}
删除节点的部分与二叉树类似:若节点无子节点,直接删除。只有一个子节点,用子节点替换。有两个子节点,则找后继节点(比当前节点大的最小节点)替换。
关键在于删除节点后的维护红黑树性质。若删除的是红色节点则没有影响,若删除的是黑色节点,则 第五性质:每条路径上的黑节点数量相同 会被破坏(删除的是根节点除外),此时需要重新平衡。依然来看TreeMap的做法:
private void fixAfterDeletion(Entry x) {
// 如果被删除的节点x不是根节点且是黑色
while (x != root && colorOf(x) == BLACK) {
if (x == leftOf(parentOf(x))) {
// 如果x是左子
// 找到x的右兄弟节点sib
Entry sib = rightOf(parentOf(x));
if (colorOf(sib) == RED) {
// 如果sib是红色
// p
// / \
// x黑 sib红
// /\
// l黑r黑
// 将sib染黑,将x的父节点p染红
setColor(sib, BLACK);
setColor(parentOf(x), RED);
// p红
// / \
// x黑 sib黑
// /\
// l黑r黑
// 左旋p
rotateLeft(parentOf(x));
// sib黑
// /\
// p红 r黑
// /\
// x黑 l黑
// 重新找到旋转后的x的右兄弟sib
sib = rightOf(parentOf(x));
// 局部形成
// p红
// /\
// x黑 sib黑
}
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
// 如果sib的子节点都是黑色
// 则将sib染红
setColor(sib, RED);
// 将x指向x的父节点p,此时p为红,循环结束
x = parentOf(x);
// 形成
// p红
// /\
// x黑 sib红
// / \
// 黑 黑
// 方法最后会讲p染黑,形成平衡
} else {
// 如果sib的子节点不都是黑色
if (colorOf(rightOf(sib)) == BLACK) {
// p红
// /\
// x黑 sib黑
// / \
// 红 黑
// 如果sib的右子是黑色,将左子也染黑,将sib染红
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
// p红
// /\
// x黑 sib红
// / \
// 黑l 黑r
// 右旋sib
rotateRight(sib);
// p红
// /\
// x黑 黑l
// \
// sib红
// \
// 黑r
// 重新找到旋转后的x的右兄弟sib
sib = rightOf(parentOf(x));
// 形成
// p红
// /\
// x黑 sib黑
// \
// 红
// \
// 黑
}
// 将sib的颜色设为p的颜色
setColor(sib, colorOf(parentOf(x)));
// 将p设为黑色
setColor(parentOf(x), BLACK);
// 将sib的右子设为黑色
setColor(rightOf(sib), BLACK);
// p黑
// /\
// x黑 sib红
// \
// 黑
// \
// 黑
// 左旋p
rotateLeft(parentOf(x));
// sib红
// / \
// p黑 黑
// / \
// x黑 黑
// 将x指向root(x为root则循环结束)
x = root;
}
} else { // symmetric
// 与上方的逻辑对称
Entry sib = leftOf(parentOf(x));
if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateRight(parentOf(x));
sib = leftOf(parentOf(x));
}
if (colorOf(rightOf(sib)) == BLACK &&
colorOf(leftOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(leftOf(sib)) == BLACK) {
setColor(rightOf(sib), BLACK);
setColor(sib, RED);
rotateLeft(sib);
sib = leftOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(leftOf(sib), BLACK);
rotateRight(parentOf(x));
x = root;
}
}
}
// 染黑
setColor(x, BLACK);
}