2-3树
2-3树是一种绝对平衡的树结构,与红黑树具有等价性,理解了2-3树就理解了红黑树。
特性
- 满足二分搜索树的基本性质,即左节点 < 根节点 < 右节点;
- 2-3树是一种绝对平衡的树;
- 节点存放一个元素也可以存放两个元素;
- 存放一个元素的节点叫做2节点
- 存放两个元素的节点叫做3节点
2-3树的一个例子
2-3树的绝对平衡性
1.向空的2-3树中添加42
2.在添加37
3.在添加12
分裂,此时绝对平衡
4.添加18,仍然绝对平衡
5. 添加6
分裂
节点12向上与37融合
此时,又进入绝对平衡状态。
6. 添加11
7. 添加5
最左侧节点进行分裂,然后与根结节点融合
此时又重新达到绝对平衡。
总结:
- 向一个2节点添加数据时,直接融合
- 向一个3节点添加数据时,先暂时形成4节点,在分裂成3个2节点,在把分裂后的新根与父节点融合
- 如果父亲节点时一个2节点,则不用继续处理
- 如果父节点时一个3节点,则融合后会产生一个4节点,需要继续向上分裂
红黑树与2-3树的等价性
由于在红黑树中每一个节点只能存储一个元素,所以使用一个黑节点表示2-3树中的2节点,用一个红节点+一个黑节点表示一个3节点(红节点在左侧),比如:
注:红黑树中的所有节点都是向左倾斜的。
转换成红黑树后如下:
红黑树
红黑树的性质
- 每个节点要么是红色,用么是黑色;
- 根节点时黑色的;
- 每一个叶子节点(最后的空节点)都是黑色;
- 如果一个节点时红色的,那么它的孩子都是黑色的;
- 从任意一个节点到叶子节点,经过的黑色节点数量都是一样的;
注:
- 红色节点表示它与它的父节点一起组合成一个2-3树中的3节点
- 红黑树能够保证绝对的黑平衡
红黑树的时间复杂度
添加:O(logn)
修改:O(logn)
删除:O(logn)
添加数据
当向一个2节点中添加一个比原节点小的值是,不用做任何调整,对应在红黑树中就是如下这种情况:
左旋转
如果是在原节点的右侧添加新的节点,那情况就不一样了
由于红黑树中红节点要向左倾斜,上图结构违反了红黑树的定义,需要进行左旋转,那么推广到更一般的情况,如下:
左旋转过程如下:
node.right=x.left;
x.left = node;
x.color=node.color;
node.color=RED;
调整后的结构如下:
右旋转
如果添加节点后,形成了下面的一个红黑结构的树,就需要进行右旋转
其中,节点12是新加的,由旋转过程如下:
node.left = x.right;
x.right = node;
x.color=node.color;
node.color = RED;
旋转后变成如下的结构:
颜色翻转
如果添加节点后形成的结构如下所示,就只需要进行颜色的翻转即可:
颜色翻转过程:
node.color = Node.RED;
node.left.color = Node.BLACK;
node.right.color = Node.BLACK;
代码实现
向红黑树中添加节点时的情况可以总结为以下几种情况(删除节点时,节点结构的调整逻辑与添加时是一致的):
/**
* 红黑树.
* */
public class RBTree<E extends Comparable<E>> {
private Node<E> root;
private int size;
public RBTree() {
this.root = null;
this.size = 0;
}
public void add(E e) {
//添加操作完成后,root节点可能发生变化
root = add(e, root);
}
//利用递归,添加节点
private Node<E> add(E e, Node<E> node) {
if (node == null) {
size ++;
return new Node(e);
}
//如果当前数据比当前节点的值小,则向左添加
if(e.compareTo(node.data) < 0) {
node.left = add(e, node.left);
} else if (e.compareTo(node.data) > 0) {
node.right = add(e, node.right);
}
if (isRead(node.right) && !isRead(node.left)) {
node = leftRotate(node);
}
if (isRead(node.left) && isRead(node.left.left)) {
node = rightRotate(node);
}
if (isRead(node.left) && isRead(node.right)) {
flipColors(node);
}
return node;
}
//左旋转
private Node<E> leftRotate(Node<E> node) {
Node<E> x = node.right;
node.right = x.left;
x.left = node;
x.color = node.color;
node.color = Node.RED;
return x;
}
//右旋转
private Node<E> rightRotate(Node<E> node) {
Node<E> x = node.left;
node.left = x.right;
x.right = node;
x.color = node.color;
node.color = Node.RED;
return x;
}
//颜色翻转
private void flipColors(Node<E> node) {
node.color = Node.RED;
node.left.color = Node.BLACK;
node.right.color = Node.BLACK;
}
//判断一个节点是否为红节点
private boolean isRead(Node<E> node) {
if (node == null) {
return Node.BLACK;
}
return node.color;
}
public int getSize() {
return size;
}
//定义节点
private static class Node<E extends Comparable<E>> {
private E data;
private Node<E> left;
private Node<E> right;
//当前节点的颜色,true=红色,false=黑色
public boolean color;
//定义两个枚举值
public static final boolean RED = true;
public static final boolean BLACK = false;
public Node(E data) {
this(data,null,null);
}
public Node(E data, Node<E> left, Node<E> right) {
this.data = data;
this.right = right;
this.left = left;
}
}
}