文章目录
红黑树
与二分搜索树和AVL树的对比
- 对于完全随机的数据,普通的二分搜索树很好用!
缺点:极端情况退化成链表(或者高度不平衡)
对于查询较多的使用情况,AVL树很好用!
红黑树牺牲了平衡性(2logn的高度)
红黑树统计性能更优(综合增删改查所有的操作)
还有类似的SplayTree伸展树
红黑树,本质上还是二分搜索树 - 1.每个节点或者是红色的,或者是黑色的
- 2.根节点是黑色的
- 3.每一个叶子节点(最后的空节点)是黑色的
- 4.如果一个节点是红色的,那么它的孩子节点都是黑色的
- 5.从任意一个节点到叶子节点,经过的黑色节点是一样的
2-3树
- 满足二分搜索树的基本性质
- 其本身不是二叉树
- 节点可以存放一个元素或者两个元素
- 节点有两个孩子或者三个孩子
2-3树如何维持绝对的平衡
红黑树
红黑树和2-3树是等价的
- 代码实现2-3树会比较复杂,因为既要操作2节点还要操作3节点,没有像之前统一操作节点方便,所以产生出红黑树。
黑结点表示2结点,红子节点+父黑节点表示3节点,所有的红色节点都是左倾斜的
将2-3树表示为红黑树
红黑树性质
- 每个节点是红色或者黑色
- 根节点是黑色的
- 每一个叶子节点(最后的空节点)是黑色的
- 如果一个节点是红色的,那么它的孩子节点都是黑色的
- 从任意一个节点到叶子节点,经过的黑色节点是一样的
- 红黑树是保持“黑平衡”的二叉树,严格意义讲不是平衡二叉树
最大高度:2logn 时间复杂度O(logn)
但是相对于AVL而言,插入元素和删除元素更加便捷
红黑树中添加新元素
- 2-3树中添加一个新元素
添加到2节点,形成3节点
添加到3节点,暂时形成4节点,再拆解
所以,新来的元素总要和当前叶子节点融合,所以永远新元素为红色
但是根节点必须是黑色的。
情况1 插入比根节点小的元素
- 先添加节点42
- 再添加节点37,此时没毛病
情况2 插入比根节点大的元素
需要左旋转
左旋转
情况3 已有3节点时,插入比根节点大的元素
- 将42的左右孩子变为黑节点,42变为红色,继续向上融合
情况4 已有3节点时,插入最小元素
右旋转
情况5 已有3节点时,插入中等元素
代码实现红黑树的添加
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;// 新创建节点为红色
}
@Override
public String toString() {
return "("+key+","+value+")";
}
}
private Node root;
private int size;
public RBTree() {
root=null;
size=0;
}
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.left;
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;
}
@Override
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;
}