B树:
在实现红黑树之前先了解一下B树,因为红黑树的一些操作的实现和B树关系密切。
B树概念:
m阶b树的性质(m>=2)
ps:b树里面只有叶子节点和度>=2的节点
b树的搜索
b树的添加
m阶b树添加上溢
当一个节点的元素个数等于m时会造成添加上溢
m阶b树删除
a 删除的节点是叶子节点
b 删除的节点是非叶子节点
c 删除下溢
注意:将父节点元素b挪下来和左右子节点合并,可能会导致父节点下溢,且这个情况可能会一直向上延续
红黑树
前述
AVL树的删除元素后恢复平衡的操作的时间复杂度可能是O(logn)级别的,可以对其进一步优化,所以红黑树应运而生(它的添加,删除,搜索平均时间度复杂都是O(logn) ,添加和删除后的恢复平衡操作是O(1)级别的)
红黑树的定义
红黑树和四阶B树的的等价性
红黑树等价成四阶B树后,该四阶B树的节点有以下四种可能:
添加
注意1:二叉搜素树新元素添加不一定添加到叶子节点,可能添加到度为1的节点
注意2:新添加的节点默认是红色这样能使红黑树的性质尽快满足
注意3:红黑树的添加是基于二叉搜索树的添加方法,不同的是在每个元素被添加后,红黑树都需要进行维护即使这棵树满足红黑树的五条性质(类似于AVLTree的添加后的操作)
节点代码
private class RBNode<T> extends Node<T> {
// 默认是红色
private boolean color = RED;
public RBNode(T element, Node<T> parent) {
super(element, parent);
}
@Override
public String toString() {
if (parent != null) {
return element + ":" + (color == true ? "r" : "h") + " p:" + parent.element;
}
return element + ":" + (color == true ? "r" : "h") + " p:";
}
}
添加的12种情况
因为红黑树是等价于四阶B树的,所以它等价的四阶B树的节点只有四种可能(上面有分析)。又因为B树添加元素都是添加到叶子节点,所有添加的所有情况就是下图的12种。(sibling:兄弟节点 parent:父节点 uncle:叔父节点(parent的兄弟节点) grand:祖父节点)
parent为黑色(四种)
不需要做任何处理
parent为红色(8种)
a:uncle是黑色 (null 默认是黑色) 4种情况
这四种情况是类似于AVLTree里的旋转操作。
LL /RR情况
LR/RL情况
b:uncle是红色 (null 默认是黑色) 4种情况
这四种情况在等价的四阶B树中是会产生上溢的,解决办法类似于B树的添加上溢(将中间元素向上合并,中间元素的左右两边的元素变成它的两个左右子节点),不同的是在红黑树中只是对一些节点进行染色而在它等价的四阶B树中是进行了元素的向上合并。
add代码
// 添加元素 默认大的放在右边 小的反在左边
// 也可以自己传入比较器
public T add(T element) {
// 判断是否有根节点
if (root == null) {
root = createNode(element, null);
size++;
afterAdd(root);
return null;
} else {
Node<T> node = root;
Node<T> parent = null;
int result = 0;
// 一直遍历 直到最后一层
while (node != null) {
parent = node;
result = compare(element, node.element);
if (result > 0) {
node = node.right;
} else if (result < 0) {
node = node.left;
} else {
return node.element;
}
}
// 根据结果判断是添加到右子树还是左子树
Node<T> newNode = createNode(element, parent);
if (result > 0) {
parent.right = newNode;
size++;
} else {
parent.left = newNode;
size++;
}
afterAdd(newNode);
return parent.element;
}
}
afteradd代码
// 添加元素后要进行的操作
@Override
protected void afterAdd(Node<T> node) {
// 添加的是根节点
if (node.parent == null) {
black(node);
return;
}
// 父节点是黑色
if (isBlack(node.parent)) {
return;
} else {// 父节点不是黑色
// 祖父节点
Node<T> grand = node.parent.parent;
// 父节点
Node<T> parent = node.parent;
// 叔父节点是红色 在四阶B树中会产生上溢
if (isRed(node.parent.getSibling())) {
black(parent);
black(parent.getSibling());
//将向上合并的节点当作一个新添加的节点去操作
afterAdd(red(grand));
return;
}
// 叔父节点不是红色
// L
if (node.parent.isLeftNode()) {
// LL
red(grand);
if (node.isLeftNode()) {
black(node.parent);
} else {// LR
black(node);
rotateleft(parent);
}
rotateRight(grand);
} else {// R
red(grand);
// RL
if (node.isLeftNode()) {
black(node);
rotateRight(parent);
rotateleft(grand);
} else {// RR
black(parent);
rotateleft(grand);
}
}
}
}
删除
上面分析过红黑树等价的四阶B树的节点情况只有四种.B树的真正删除只会在叶子节点中进行也就是最后一层。所以可以分成以下二种情况来讨论.
**删除的节点是红色 **
例如删除的是17,33,50,72
删除的节点是黑色 (3种情况)
a:删除拥有一个红色子节点的黑色子节点
例如删除46,76
b.删除黑色叶子节点
在等价的四阶B树中被删除的黑色叶子节点有12种可能(和添加类似的分析),这12种可能是对称的,根据在父节点左右划分各6种。下面对黑色叶子节点是父节点的右子节点进行讨论。
【88是要被删除的黑色叶子节点】
1.兄弟节点是红色(包含1种种情况)
2 兄弟节点是黑色的(包含5种情况)
兄 节点
兄弟节点没有红色子节点
remove代码
// 移除元素
public void remove(T element) {
Node<T> node = getNode(element);
remove(node);
}
/**
* 移除元素后要进行的操作
*
* @param node 删除的节点或者是用于替代的节点
*
*/
protected void afterRemove(Node<T> node) {
}
// 根据元素找到节点
private Node<T> getNode(T element) {
Node<T> node = root;
while (node != null) {
int result = compare(node.element, element);
if (result == 0) {
return node;
} else if (result < 0) {
node = node.right;
} else {
node = node.left;
}
}
return null;
}
// 移除节点
private void remove(Node<T> node) {
// 用于取代的节点
Node<T> replace;
// 删除节点度为2的节点
if (node.hasTwoChildren()) {
// 直接将它的前驱节点的值赋给该节点 然后删除前驱节点
node.element = getpredecessor(node).element;
// 前驱节点 后继节点的度只能为1 或者0
// 所以直接赋值给node 通过后续删除度为0或1节点来间接删除
node = getpredecessor(node);
}
// 删除度为0或1的节点
if (node.isLeaf()) {
if (node.parent == null) {
root = null;
return;
} else {
if (node.isLeftNode()) {
node.parent.left = null;
} else {
node.parent.right = null;
}
size--;
// node是被删除的节点
afterRemove(node);
return;
}
} else {
if (node.parent == null) {
if (node.left != null) {
replace = node.left;
root = replace;
} else {
replace = node.right;
root = replace;
}
} else {
if (node.isLeftNode()) {
if (node.left != null) {
replace = node.left;
replace.parent = node.parent;
node.parent.left = replace;
} else {
replace = node.right;
replace.parent = node.parent;
node.parent.left = replace;
}
} else {
if (node.left != null) {
replace = node.left;
replace.parent = node.parent;
node.parent.left = replace;
} else {
replace = node.right;
replace.parent = node.parent;
node.parent.left = replace;
}
}
}
size--;
//replace是替代节点
afterRemove(replace);
return;
}
}
affterRemove代码
// 传入一个参数的afterRemove方法 该参数既可以是被删除节点也可以是替代节点
// 删除度为2的节点会去删除它的前驱或后继节点
// 所以其实真正被删除的节点只有可能是度为0或1的节点
protected void afterRemove(Node<T> node) {
// 被删除节点的父节点
Node<T> parent = node.parent;
// 被删除的节点是根节点
if (parent == null)
return;
// 如果替代节点是红色或者被删除的节点是红色
if (isRed(node)) {
black(node);
return;
}
// 被删除的节点是黑色叶子节点
// 判断被删除的节点是左节点还是右节点
boolean left = parent.left == null || node.isLeftNode();
// 获取兄弟节点
Node<T> sibling = left ? parent.right : parent.left;
// 黑色叶子节点的两种可能代码是对称的
// 黑色叶子节点是右节点
if (!left) {
// 兄弟节点是红色
if (isRed(sibling)) {
red(parent);
black(sibling);
rotateRight(parent);
// 更换兄弟节点
sibling = parent.left;
}
// 兄弟节点是黑色
// 兄弟节点没有一个红色子节点
if (isBlack(sibling.left) && isBlack(sibling.right)) {
// 父节点的颜色是否为黑色
boolean parentColor = isBlack(parent);
// 将父节点变成黑色
black(parent);
// 将兄弟节点变成红色
red(sibling);
if (parentColor) {
afterRemove(parent);
return;
}
} else {
// 兄弟节点至少有一个红色子节点
// 兄弟节点的左子节点是黑色
if (isBlack(sibling.left)) {
rotateleft(sibling);
sibling = parent.left;
}
changeColor(sibling, colorOf(parent));
black(parent);
black(sibling.left);
rotateRight(parent);
}
} else {
// 黑色叶子节点是左节点
// 兄弟节点是红色
if (isRed(sibling)) {
red(parent);
black(sibling);
rotateleft(parent);
// 更换兄弟节点
sibling = parent.right;
}
// 兄弟节点是黑色
// 兄弟节点没有一个红色子节点
if (isBlack(sibling.left) && isBlack(sibling.right)) {
// 父节点的颜色是否为黑色
boolean parentColor = isBlack(parent);
// 将父节点变成黑色
black(parent);
// 将兄弟节点变成红色
red(sibling);
if (parentColor) {
afterRemove(parent);
return;
}
} else {
// 兄弟节点至少有一个红色子节点
// 兄弟节点的左子节点是黑色
if (isBlack(sibling.right)) {
rotateRight(sibling);
sibling = parent.right;
}
changeColor(sibling, colorOf(parent));
black(parent);
black(sibling.right);
rotateleft(parent);
}
}
}
// 传入两个参数的afterRemove方法
// 删除度为2的节点会去删除它的前驱或后继节点
// 所以其实真正被删除的节点只有可能是度为0或1的节点
protected void afterRemove1(Node<T> node, Node<T> replace) {
// 被删除节点的父节点
Node<T> parent = node.parent;
// 被删除的节点是红色
if (isRed(node))
return;
// 被删除的节点是根节点
if (parent == null)
return;
// 如果替代节点是红色
if (isRed(replace)) {
black(replace);
return;
}
// 被删除的节点是黑色叶子节点
// 判断被删除的节点是左节点还是右节点
boolean left = parent.left == null || node.isLeftNode();
// 获取兄弟节点
Node<T> sibling = left ? parent.right : parent.left;
// 黑色叶子节点的两种可能代码是对称的
// 黑色叶子节点是右节点
if (!left) {
// 兄弟节点是红色
if (isRed(sibling)) {
red(parent);
black(sibling);
rotateRight(parent);
// 更换兄弟节点
sibling = parent.left;
}
// 兄弟节点是黑色
// 兄弟节点没有一个红色子节点
if (isBlack(sibling.left) && isBlack(sibling.right)) {
// 父节点的颜色是否为黑色
boolean parentColor = isBlack(parent);
// 将父节点变成黑色
black(parent);
// 将兄弟节点变成红色
red(sibling);
//父节点原先颜色是黑色 则将父节点传入afteRemove方法来维持红黑树性质
if (parentColor) {
afterRemove(parent);
return;
}
} else {
// 兄弟节点至少有一个红色子节点
// 兄弟节点的左子节点是黑色
if (isBlack(sibling.left)) {
rotateleft(sibling);
sibling = parent.left;
}
changeColor(sibling, colorOf(parent));
black(parent);
black(sibling.left);
rotateRight(parent);
}
} else {
// 黑色叶子节点是左节点
// 兄弟节点是红色
if (isRed(sibling)) {
red(parent);
black(sibling);
rotateleft(parent);
// 更换兄弟节点
sibling = parent.right;
}
// 兄弟节点是黑色
// 兄弟节点没有一个红色子节点
if (isBlack(sibling.left) && isBlack(sibling.right)) {
// 父节点的颜色是否为黑色
boolean parentColor = isBlack(parent);
// 将父节点变成黑色
black(parent);
// 将兄弟节点变成红色
red(sibling);
//父节点原先颜色是黑色 则将父节点传入afteRemove方法来维持红黑树性质
if (parentColor) {
afterRemove(parent);
return;
}
} else {
// 兄弟节点至少有一个红色子节点
// 兄弟节点的左子节点是黑色
if (isBlack(sibling.right)) {
rotateRight(sibling);
sibling = parent.right;
}
changeColor(sibling, colorOf(parent));
black(parent);
black(sibling.right);
rotateleft(parent);
}
}
}
}
为啥维持那五条性质红黑树就能保持平衡
红黑树的平均时间复杂度
红黑树和AVL树的对比
RBTree代码
package com.ldg.tree;
public class RBTree<T> extends BalanceBinarySearchTree<T> {
// 颜色常量
private static final boolean RED = true;
private static final boolean BLACK = false;
private class RBNode<T> extends Node<T> {
// 默认是红色
private boolean color = RED;
public RBNode(T element, Node<T> parent) {
super(element, parent);
}
@Override
public String toString() {
if (parent != null) {
return element + ":" + (color == true ? "r" : "h") + " p:" + parent.element;
}
return element + ":" + (color == true ? "r" : "h") + " p:";
}
}
/**
* 判断节点的颜色 空节点默认是黑色的
*
* @param node
* @return false:黑色 , true:红色
*/
private boolean colorOf(Node<T> node) {
return node == null ? BLACK : ((RBNode) node).color;
}
/**
* 判断节点是否为黑色
*
* @param node
* @return
*/
private boolean isBlack(Node<T> node) {
return colorOf(node) == BLACK;
}
/**
* 判断节点是否为红色
*
* @param node
* @return
*/
private boolean isRed(Node<T> node) {
return colorOf(node) == RED;
}
/**
* 改变节点的颜色
*
* @param node
* @param color
* @return 返回改变后的节点
*/
private RBNode<T> changeColor(Node<T> node, boolean color) {
if (node != null)
((RBNode) node).color = color;
return (RBNode) node;
}
/**
* 将节点变成黑色
*
* @param node
* @return
*/
private RBNode<T> black(Node<T> node) {
return changeColor(node, BLACK);
}
/**
* 将节点变成红色
*
* @param node
* @return
*/
private RBNode<T> red(Node<T> node) {
return changeColor(node, RED);
}
// 创建自己的子节点
@Override
protected Node<T> createNode(T element, Node<T> parent) {
return new RBNode(element, parent);
}
// 添加元素后要进行的操作
@Override
protected void afterAdd(Node<T> node) {
// 添加的是根节点
if (node.parent == null) {
black(node);
return;
}
// 父节点是黑色
if (isBlack(node.parent)) {
return;
} else {// 父节点不是黑色
// 祖父节点
Node<T> grand = node.parent.parent;
// 父节点
Node<T> parent = node.parent;
// 叔父节点是红色
if (isRed(node.parent.getSibling())) {
black(parent);
black(parent.getSibling());
afterAdd(red(grand));
return;
}
// 叔父节点不是红色
// L
if (node.parent.isLeftNode()) {
// LL
red(grand);
if (node.isLeftNode()) {
black(node.parent);
} else {// LR
black(node);
rotateleft(parent);
}
rotateRight(grand);
} else {// R
red(grand);
// RL
if (node.isLeftNode()) {
black(node);
rotateRight(parent);
rotateleft(grand);
} else {// RR
black(parent);
rotateleft(grand);
}
}
}
}
// 传入一个参数的afterRemove方法 该参数既可以是被删除节点也可以是替代节点
// 删除度为2的节点会去删除它的前驱或后继节点
// 所以其实真正被删除的节点只有可能是度为0或1的节点
protected void afterRemove(Node<T> node) {
// 被删除节点的父节点
Node<T> parent = node.parent;
// 被删除的节点是根节点
if (parent == null)
return;
// 如果替代节点是红色或者被删除的节点是红色
if (isRed(node)) {
black(node);
return;
}
// 被删除的节点是黑色叶子节点
// 判断被删除的节点是左节点还是右节点
boolean left = parent.left == null || node.isLeftNode();
// 获取兄弟节点
Node<T> sibling = left ? parent.right : parent.left;
// 黑色叶子节点的两种可能代码是对称的
// 黑色叶子节点是右节点
if (!left) {
// 兄弟节点是红色
if (isRed(sibling)) {
red(parent);
black(sibling);
rotateRight(parent);
// 更换兄弟节点
sibling = parent.left;
}
// 兄弟节点是黑色
// 兄弟节点没有一个红色子节点
if (isBlack(sibling.left) && isBlack(sibling.right)) {
// 父节点的颜色是否为黑色
boolean parentColor = isBlack(parent);
// 将父节点变成黑色
black(parent);
// 将兄弟节点变成红色
red(sibling);
if (parentColor) {
afterRemove(parent);
return;
}
} else {
// 兄弟节点至少有一个红色子节点
// 兄弟节点的左子节点是黑色
if (isBlack(sibling.left)) {
rotateleft(sibling);
sibling = parent.left;
}
changeColor(sibling, colorOf(parent));
black(parent);
black(sibling.left);
rotateRight(parent);
}
} else {
// 黑色叶子节点是左节点
// 兄弟节点是红色
if (isRed(sibling)) {
red(parent);
black(sibling);
rotateleft(parent);
// 更换兄弟节点
sibling = parent.right;
}
// 兄弟节点是黑色
// 兄弟节点没有一个红色子节点
if (isBlack(sibling.left) && isBlack(sibling.right)) {
// 父节点的颜色是否为黑色
boolean parentColor = isBlack(parent);
// 将父节点变成黑色
black(parent);
// 将兄弟节点变成红色
red(sibling);
if (parentColor) {
afterRemove(parent);
return;
}
} else {
// 兄弟节点至少有一个红色子节点
// 兄弟节点的左子节点是黑色
if (isBlack(sibling.right)) {
rotateRight(sibling);
sibling = parent.right;
}
changeColor(sibling, colorOf(parent));
black(parent);
black(sibling.right);
rotateleft(parent);
}
}
}
// 传入两个参数的afterRemove方法
// 删除度为2的节点会去删除它的前驱或后继节点
// 所以其实真正被删除的节点只有可能是度为0或1的节点
protected void afterRemove1(Node<T> node, Node<T> replace) {
// 被删除节点的父节点
Node<T> parent = node.parent;
// 被删除的节点是红色
if (isRed(node))
return;
// 被删除的节点是根节点
if (parent == null)
return;
// 如果替代节点是红色
if (isRed(replace)) {
black(replace);
return;
}
// 被删除的节点是黑色叶子节点
// 判断被删除的节点是左节点还是右节点
boolean left = parent.left == null || node.isLeftNode();
// 获取兄弟节点
Node<T> sibling = left ? parent.right : parent.left;
// 黑色叶子节点的两种可能代码是对称的
// 黑色叶子节点是右节点
if (!left) {
// 兄弟节点是红色
if (isRed(sibling)) {
red(parent);
black(sibling);
rotateRight(parent);
// 更换兄弟节点
sibling = parent.left;
}
// 兄弟节点是黑色
// 兄弟节点没有一个红色子节点
if (isBlack(sibling.left) && isBlack(sibling.right)) {
// 父节点的颜色是否为黑色
boolean parentColor = isBlack(parent);
// 将父节点变成黑色
black(parent);
// 将兄弟节点变成红色
red(sibling);
//父节点原先颜色是黑色 则将父节点传入afteRemove方法来维持红黑树性质
if (parentColor) {
afterRemove(parent);
return;
}
} else {
// 兄弟节点至少有一个红色子节点
// 兄弟节点的左子节点是黑色
if (isBlack(sibling.left)) {
rotateleft(sibling);
sibling = parent.left;
}
changeColor(sibling, colorOf(parent));
black(parent);
black(sibling.left);
rotateRight(parent);
}
} else {
// 黑色叶子节点是左节点
// 兄弟节点是红色
if (isRed(sibling)) {
red(parent);
black(sibling);
rotateleft(parent);
// 更换兄弟节点
sibling = parent.right;
}
// 兄弟节点是黑色
// 兄弟节点没有一个红色子节点
if (isBlack(sibling.left) && isBlack(sibling.right)) {
// 父节点的颜色是否为黑色
boolean parentColor = isBlack(parent);
// 将父节点变成黑色
black(parent);
// 将兄弟节点变成红色
red(sibling);
//父节点原先颜色是黑色 则将父节点传入afteRemove方法来维持红黑树性质
if (parentColor) {
afterRemove(parent);
return;
}
} else {
// 兄弟节点至少有一个红色子节点
// 兄弟节点的左子节点是黑色
if (isBlack(sibling.right)) {
rotateRight(sibling);
sibling = parent.right;
}
changeColor(sibling, colorOf(parent));
black(parent);
black(sibling.right);
rotateleft(parent);
}
}
}
}
运行效果