一、前言
在学习红黑树之前需要学习B树,它们之间有着密切的关系,因为红黑树可以和4阶B树完美匹配。我的理解是4阶B树里最多含4个分叉。
我们会发现B树有一些特点:
1.比较矮
2.平衡
3.拥有二叉搜索树的一些性质
4.一个节点能存储多个元素
一个m阶的B树具有以下性质:
根节点:1<= x <= m-1
非根节点:[m/2] - 1 <= x <= m-1
所以4阶B树的根节点个数 1<= x <= 3 , 非根节点的个数:1<= x <= 3, [ ] 这个符号代表的是向上取整。
既然是这样那么添加删除的时候,就会出现问题呗!
添加的时候可能会出现上溢,解决办法:找到该节点的最中间的元素,然后该节点就一分为二。这个例子中就是把95放到父节点中去,而90和98、100就变成了两个节点。
删除的时候可能会发生下溢:
解决办法:
分两种情况:
当删除的节点是非叶子节点时,和二叉搜索树的做法是一样的,找到该节点的前驱节点或者后继节点,然后替换掉60,最后删除55或者70,你会发现最后删除的节点还是叶子节点。
当删除的节点是叶子节点时,当兄弟节点个数大于1时,比如删除50,做法就很简单,让根节点52左旋转就行了;当兄弟节点只有一个节点时,例如:删除10,那么根节点20就需要和子节点合并。
二、红黑树的添加
首先一颗树是红黑树必须满足5个性质:
1.节点的颜色必须是红色或者黑色
2.根节点是黑色
3.叶子节点是黑色
4.红色节点的父节点和叶子节点必须是黑色
5.从根节点到任意一个叶子节点所包含的黑色节点个数是一样的。
当一颗二叉搜索树满足以上性质后,我们就说这个树是红黑树,且是平衡的。
红黑树的等价变换:
下面就来分析红黑树的添加:
我们知道添加的节点一定是叶子节点,这个非常重要。
建议新添加的节点默认为 RED,这样能够让红黑树的性质尽快满足(性质 1、2、3、5 都满足,性质 4 不一定)
那么问题来了,添加一共有多少种情况呢?答案是12,从上图中就能数出来(17的左右孩子,33的左右孩子...),但实际我们会将这12种进行分类。
第一种情况:添加的节点的父节点是黑色节点,不需要做任何处理,因为它还是红黑树。
第二种情况:当添加的节点是52或60时,我们会对它们的祖父节点进行旋转,如果是52就对46进行左旋转,如果是60就对76进行右旋转,旋转的代码我们在AVL树中已经写过了。
第三种情况:当添加的节点是48,我们就对50进行右旋,记住一点对一个节点进行旋转,那么它的高度一定是变小,然后再对46进行左旋。你会发现学会了旋转,再学红黑树就没有那么难了;同理当添加的节点是74是,就对72进行左旋,然后对76进行右旋。
第四种情况:当添加的节点是10、20、30、36时,我们做以下处理:
添加方法:
package 红黑树;
import java.util.Comparator;
public class RBTree<E>{
private static final boolean RED = false;
private static final boolean BLACK = true;
private int size;
private RBNode<E> root;
private Comparator<E> comparator;
private static class RBNode<E>{
boolean color = RED;
E element;
RBNode<E> left;
RBNode<E> right;
RBNode<E> parent;
public RBNode(E element,RBNode<E> parent){
this.element = element;
this.parent = parent;
}
}
public void add(E element) {
if(element == null) return;
// 添加第一个节点
if (root == null) {
root = new RBNode<>(element, null);
size++;
afterAdd(root);
return;
}
// 添加的不是第一个节点
// 找到父节点
RBNode<E> parent = root;
RBNode<E> node = root;
int cmp = 0;
while (node != null){
cmp = comparator.compare(element, node.element);
parent = node;
if (cmp > 0) {
node = node.right;
} else if (cmp < 0) {
node = node.left;
} else { // 相等
node.element = element;
return;
}
}
// 看看插入到父节点的哪个位置
RBNode<E> newNode = new RBNode<>(element, parent);
if (cmp > 0) {
parent.right = newNode;
} else {
parent.left = newNode;
}
afterAdd(newNode);
size++;
}
protected void afterAdd(RBNode<E> node) {
RBNode<E> parent = node.parent;
// 添加的是根节点 或者 上溢到达了根节点
if (parent == null) {
black(node);
return;
}
// 如果父节点是黑色,直接返回
if (isBlack(parent)) return;
// 叔父节点
RBNode<E> uncle = sibling(node);
// 祖父节点
RBNode<E> grand = red(parent.parent);
if (isRed(uncle)) { // 叔父节点是红色【B树节点上溢】
black(parent);
black(uncle);
// 把祖父节点当做是新添加的节点
afterAdd(grand);
return;
}
// 叔父节点不是红色
if (isLeftChild(parent)) { // L
if (isLeftChild(node)) { // LL
black(parent);
} else { // LR
black(node);
lefTurn(parent);
}
rightTurn(grand);
} else { // R
if (isLeftChild(node)) { // RL
black(node);
rightTurn(parent);
} else { // RR
black(parent);
}
lefTurn(grand);
}
}
private void lefTurn(RBNode<E> grand) {
RBNode<E> parent = grand.right; //节点p
RBNode<E> child = parent.left; //子树T1
grand.right = child;
parent.left = grand;
//让p成为根节点
parent.parent = grand.parent;
if(grand == grand.parent.left){
grand.parent.left = parent;
}else if(grand == grand.parent.right){
grand.parent.right = parent;
}else{
root = parent;
}
if(child!=null){
child.parent = grand; //更新t1的parent
}
grand.parent = parent; //更新grand的parent
}
private void rightTurn(RBNode<E> grand) {
RBNode<E> parent = grand.left; //节点p
RBNode<E> child = parent.right; //子树T2
grand.left = child;
parent.right = grand;
//让p成为根节点
parent.parent = grand.parent;
if(grand == grand.parent.left){
grand.parent.left = parent;
}else if(grand == grand.parent.right){
grand.parent.right = parent;
}else{
root = parent;
}
//更新parent属性
if(child!=null){
child.parent = grand;
}
grand.parent = parent;
}
private RBNode<E> color(RBNode<E> node, boolean color) {
if (node == null) return node;
node.color = color;
return node;
}
private RBNode<E> red(RBNode<E> node) {
return color(node, RED);
}
private RBNode<E> black(RBNode<E> node) {
return color(node, BLACK);
}
private boolean colorOf(RBNode<E> node) {
return node == null ? BLACK : node.color;
}
private boolean isBlack(RBNode<E> node) {
return colorOf(node) == BLACK;
}
private boolean isRed(RBNode<E> node) {
return colorOf(node) == RED;
}
public boolean isLeftChild(RBNode<E> node) {
return node.parent != null && node == node.parent.left;
}
public boolean isRightChild(RBNode<E> node) {
return node.parent != null && node == node.parent.right;
}
public RBNode<E> sibling(RBNode<E> node) {
if(node.parent == null) return null;
if (isLeftChild(node)) {
return node.parent.right;
}
if (isRightChild(node)) {
return node.parent.left;
}
return null;
}
}