前言
时隔半年多,再次解读红黑树,之前的文章对红黑树(Red Black Tree)进行了只是对红黑树的性质直接进行了说明,但并没有分析红黑树到底是怎么产生的,红和黑又是如何形成的。
在《算法导论》一书中对红黑树的性质进行了深奥解读,对于初学者来说非常不友好,对于我自己来说也只是记得性质,只知其然,而不知其所以然。
A red-black tree is a binary tree that satisfies the following red-black properties:
- Every node is either red or black.
- The root is black.
- Every leaf(NIL)is black.
- If a node is red,then both its children are black.
- For each node, all simple paths from the node to descendant leaves contains the same number of black.
翻译成对应的中文:
1.每个节点或者是红色的,或者是黑色的
2.根节点是黑色的
3.每一个叶子节点(最后的空节点)是黑色的
4.如果一个节点是红色的,那么他的孩子节点 都是黑色的
5.从任意一个节点到叶子节点,经过的黑色节点是一样的
2-3树
满足二分搜索树的基本性质,节点可以存放一个元素或者两个元素。如下图:所谓的2-3树就是每个节点有2个或者3个孩子。2-3树是一颗绝对平衡的树。
2-3树如何维持绝对平衡
对于2-3树来说,添加一个节点是找到最后一个叶子节点并与之融合,如下图
42本是一个2节点,通过融合37后变成了一个3节点,很显然此时这个2-3树依然是一棵绝对平衡的树。如果再向这个2-3树添加12,12比37小,按道理12是添加进37的左子树去,然而37的左子树为空,要记住在2-3树中添加一个节点,这个节点永远不会去一个空的位置,只会和最后找到的叶子节点做融合。这里找到的叶子节点是一个3节点,与之融合,这样成为了4节点,但在2-3树中不存在4节点,因此将其分裂,如下图:
这棵树依然保持绝对平衡,在这棵树的基础上再添加一个18,18与最后找到的叶子节点也就是12做一个融合,12是一个二节点,还有空间,18与之融合。如下图:
此时这个2-3树依然保持绝对平衡,此时再添加一个6节点,6与最后找到的叶子节点也就是12和18这个3节点做融合,形成一个临时4节点,而2-3树显然不存在4节点,需要进行拆解。拆解后的根节点12需要和它的父节点37进行融合,而37是一个2节点,故12和37可以直接融合成一个3节点。进而原来12的孩子节点就变成这个新的3节点的左孩子节点和中间孩子节点。如下图:
此时这个2-3树依然保持绝对平衡,此时再添加一个节点11,找到最后一个叶子节点6(6是一个二节点)直接与之融合。如下图:
此时这个2-3树依然保持绝对平衡,如果再来一个新节点5,找到最后一个叶子节点6和11做融合暂时形成一个4节点,然后进行拆解,对于拆解后新的子树的根节点6相应的融合到父亲节点中去,原本父亲节点是一个3节点,此时形成一个临时4节点,如下图:
由于在2-3树中最多只能是3节点,所以这个4节点还要继续进行分裂,和之前一样,把这个4节点的3个元素化成3个2节点,如下图:
之前的4节点本是一个根节点,化成这样一个形状后,也就不需要继续向上融合父亲节点了,因为根节点已经到头了。至此2-3树的添加操作到此完成。这个2-3树依然保持绝对平衡。
红黑树
对于2-3树中添加一个新元素:
或者添加进2-节点,形成一个3-节点,或者添加进3-节点,暂时形成一个4-节点,最后永远添加红色节点。
红黑树与2-3树的等价性:对应2-3树中的2节点和3节点转换为红黑树如下图:
在红黑树中所有的红色节点都是左倾斜的。在如下图的2-3树中有3个3节点,每一个3节点就会相应产生一个红色节点,因此对应红黑树中有3个红色节点。红黑树依然满足二分搜索树的性质。
红黑树中添加新元素
红黑树添加新元素有三种方式:
1.向3节点中添加一个最大的数(c>a>b)
2.向3节点中添加一个最小的数(c<b<a)
3.向3节点中添加一个中间的数(a>c>b)
向红黑树中添加新节点有3个子过程,分别是左旋转,右旋转,颜色翻转。红黑树添加新元素三种方式过程如下图:
红黑树维护的时机和AVL树一样,都是先使用二分搜索树的基本逻辑,把新的节点添加进红黑树中,之后再来进行红黑树性质的维护。在维护完成以后,将维护后新的根节点返回给递归调用的上一层,从上一层的角度看是否需要维护新的节点,依此类推。
三个子过程代码实现如下:
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 getSize() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
//判断节点Node的颜色
private boolean isRed(Node node) {
if (node == null) {
return BLACK;
}
return node.color;
}
//左旋转
private Node leftRotate(Node node) {
Node x = node.right;
//左旋转
node.right = x.right;
x.left = node;
x.color = node.color;
node.color = RED;
return x;
}
//右旋转
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;
}
//颜色翻转
private void flipColors(Node node) {
node.color = RED;
node.left.color = BLACK;
node.right.color = BLACK;
}
//向红黑树中添加新的元素(key,value)
public void add(K key, V value) {
root = add(root, key, value);
root.color = BLACK;//最终根节点为黑色节点
}
//向以node为跟的红黑树中插入元素(key,value),递归算法
//返回插入新节点后红黑树的根
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.left) && !isRed(node.right))
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;
}
}