红黑树插入删除调整策略分析与实现
一、 由来
红黑树(英语:Red–black tree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型用途是实现关联数组。它在1972年由鲁道夫·贝尔发明,被称为"对称二叉B树",它现代的名字源于Leo J. Guibas和罗伯特·塞奇威克于1978年写的一篇论文。红黑树的结构复杂,但它的操作有着良好的最坏情况运行时间,并且在实践中高效:它可以在O(logn)时间内完成查找、插入和删除,这里的n是树中元素的数目。
二、用途和好处
红黑树和AVL树一样都对插入时间、删除时间和查找时间提供了最好可能的最坏情况担保。这不只是使它们在时间敏感的应用,如实时应用(real time application)中有价值,而且使它们有在提供最坏情况担保的其他数据结构中作为基础模板的价值;例如,在计算几何中使用的很多数据结构都可以基于红黑树实现。
红黑树在函数式编程中也特别有用,在这里它们是最常用的持久数据结构(persistent data structure)之一,它们用来构造关联数组和集合,每次插入、删除之后它们能保持为以前的版本。除了O(logn)的时间之外,红黑树的持久版本对每次插入或删除需要O(logn的空间。
红黑树是2-3-4树的一种等同。换句话说,对于每个2-3-4树,都存在至少一个数据元素是同样次序的红黑树。在2-3-4树上的插入和删除操作也等同于在红黑树中颜色翻转和旋转。这使得2-3-4树成为理解红黑树背后的逻辑的重要工具,这也是很多介绍算法的教科书在红黑树之前介绍2-3-4树的原因,尽管2-3-4树在实践中不经常使用。
红黑树相对于AVL树来说,牺牲了部分平衡性以换取插入/删除操作时少量的旋转操作,整体来说性能要优于AVL树
三、性质:
红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
1.节点是红色或黑色。
2.根是黑色。
3.所有叶子都是黑色(叶子是NIL节点)。
4.每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
5.从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
四、插入调整
需要站在祖父节点向下观察
情况1:子节点均为红
调整策略:这里采用红色上浮调整策略:将2号节点与3号节点染为黑色,1号节点染为红色
情况2:一方子节点为黑色,另一方子节点为红色
2.1 LL型冲突
调整策略:进行右旋后父节点染红,左右子节点染黑;
2.2LR型冲突
调整策略:先对冲突的位置进行左旋变为LL型冲突,然后再整体进行右旋,最后父节点染红,子节点染黑
3.RR型、RL型与上同理
五、删除调整
在进行删除调整之前,我们先明确一个概念,我们将叶节点,即NIL节点定义为双重黑节点。删除调整的目的,就是将双重黑节点调整掉。
不能确定颜色的节点用灰色表示
x表示双重黑节点
图中表示的为整体树中的一部分
情况1:双重黑节点的兄弟节点为黑色,且兄弟节点无红色子节点
调整策略:双重黑节点,兄弟节点黑色减一重黑,父节点颜色加一重黑
情况2:双重黑节点的兄弟节点为黑色,且兄弟节点有红色子节点
2.1 红色子节点 与兄弟节点同侧 LL/RR类型
整棵树进行左(右)旋,然后新根节点改为原根节点的颜色,原根节点与新叔叔节点改为黑色
2.2 红色子节点 与兄弟节点异侧且与兄弟节点同侧的子节点为黑色 LL/RR类型
调整策略:
1.先对子树进行小左(右)旋,然后旧根节点染红,新根节点染黑。
2.整棵树进行左(右)旋,然后新根节点改为原根节点的颜色,原根节点与新叔叔节点改为黑色
对于情况2总结而言,LL/RR类型的判断优先于LR/RL类型
情况3:双重黑节点的兄弟节点为红色
调整策略:通过左(右)旋转,新旧根节点颜色对调。调整成情况1或情况2再做处理
六、实现代码
public class RBTree {
class Node {
int val, color;
Node left, right;
}
// 使用NIL节点来充当叶节点
Node NIL;
Node root;
public RBTree() {
NIL = new Node();
NIL.val = -1;
NIL.color = 1;
NIL.left = NIL.right = NIL;
root = NIL;
}
// 创建节点
private Node getNewNode(int val) {
Node p = new Node();
p.val = val;
p.color = 0;
p.left = p.right = NIL;
return p;
}
//判断有没有红色孩子
private boolean has_red_child(Node tree) {
return tree.left.color == 0 || tree.right.color == 0;
}
//左旋
private Node left_rotate(Node tree) {
Node temp = tree.right;
tree.right = temp.left;
temp.left = tree;
return temp;
}
//右旋
private Node right_rotate(Node tree) {
Node temp = tree.left;
tree.left = temp.right;
temp.right = tree;
return temp;
}
//寻找前驱
private Node preNode(Node tree) {
Node p = tree.left;
while (p.right != null) {
p = p.right;
}
return p;
}
//删除
public void erase(int val) {
root = erase(root, val);
}
private Node erase(Node tree, int val) {
tree = __erase(tree, val);
tree.color = 1;
return tree;
}
private Node __erase(Node tree, int val) {
if (tree == NIL) return tree;
if (val < tree.val) {
tree.left = __erase(tree.left, val);
} else if (val > tree.val) {
tree.right = __erase(tree.right, val);
} else {
if (tree.left == NIL || tree.right == NIL) {
Node temp = tree.left == NIL ? tree.right : tree.left;
temp.color += tree.color;
tree = temp;
return tree;
} else {
Node temp = preNode(tree);
tree.val = temp.val;
tree.left = __erase(tree.left, temp.val);
}
}
return erase_maintion(tree);
}
//删除调整
private Node erase_maintion(Node tree) {
if (tree.left.color != 2 && tree.right.color != 2) return tree;
// 兄弟为红,旋转树,新根节点转为黑,原根节点转为红
if (has_red_child(tree)) {
int flag = 0;
tree.color = 0;
if (tree.left.color == 0) {
tree = right_rotate(tree);
flag = 1;
} else {
tree = left_rotate(tree);
}
tree.color = 1;
if (flag == 1) tree.right = erase_maintion(tree.right);
else tree.left = erase_maintion(tree.left);
return tree;
}
// 兄弟为黑色并且没有红色子节点,子节点减黑,根节点加黑
if (tree.left.color == 1 && !has_red_child(tree.left)
|| tree.right.color == 1 && !has_red_child(tree.right)) {
tree.color += 1;
tree.left.color -= 1;
tree.right.color -= 1;
return tree;
}
// 兄弟节点为黑并且有红色子节点
// |-- 左子树为黑色
// |-- 左子树的右子树为红色且左子树节点为黑 LR
// |-- 子树小左旋,新节点转黑,原节点转红,进入LL形态
// |-- 左子树的左子树为红色 LL
// |-- 整树右旋,新节点改为原根节点的颜色,原根节点已经新叔叔节点转为黑色
// |-- 右子树为黑色
// |-- 右子树的左子树为红色且右子树节点为黑 RL
// |-- 子树小右旋,新节点转黑,原节点转红,进入RR形态
// |-- 右子树的右子树为红色 RR
// |-- 整树左旋,新节点改为原根节点的颜色,原根节点已经新叔叔节点转为黑色
if (tree.left.color == 1) {
tree.right.color = 1;
if (tree.left.left.color != 0) {
tree.left.color = 0;
tree.left = left_rotate(tree.left);
tree.left.color = 1;
}
tree.left.color = tree.color;
tree = right_rotate(tree);
} else {
tree.left.color = 1;
if (tree.right.right.color != 0) {
tree.right.color = 0;
tree.right = right_rotate(tree.right);
tree.right.color = 1;
}
tree.right.color = tree.color;
tree = left_rotate(tree);
}
tree.left.color = 1;
tree.right.color = 1;
return tree;
}
//添加
public void insert(int val) {
root = insert(root, val);
}
private Node insert(Node tree, int val) {
tree = __insert(tree, val);
tree.color = 1;
return tree;
}
private Node __insert(Node tree, int val) {
if (tree == NIL) {
return getNewNode(val);
}
if (val < tree.val) {
tree.left = __insert(tree.left, val);
} else if (val > tree.val) {
tree.right = __insert(tree.right, val);
}
return insert_maintain(tree);
}
//添加调整
private Node insert_maintain(Node tree) {
if (!has_red_child(tree)) return tree;
//节点双红
if (tree.left.color == 0 && tree.right.color == 0) {
if (!has_red_child(tree.left) && !has_red_child(tree.right)) return tree;
tree.color = 0;
tree.left.color = tree.right.color = 1;
return tree;
}
if (tree.left.color == 0 && !has_red_child(tree.left)) return tree;
if (tree.right.color == 0 && !has_red_child(tree.right)) return tree;
// 左子树失衡
if (tree.left.color == 0) {
if (tree.left.right.color == 0) {
tree.left = left_rotate(tree.left);
}
tree = right_rotate(tree);
} else {
if (tree.right.left.color == 0) {
tree.right = right_rotate(tree.right);
}
tree = left_rotate(tree);
}
tree.color = 0;
tree.left.color = tree.right.color = 1;
return tree;
}
//打印输出
public void preorder() {
preorder(root, root.val, 0);
}
private void preorder(Node tree, int val, int flag) {
if (tree == NIL) return;
if (flag == 0) {
System.out.printf("%d is root, color is %s\n", val, tree.color == 0 ? "red" : "black");
} else {
System.out.printf("%d is %d's %s child, color is %s\n"
, tree.val, val, flag == 1 ? "right" : "left", tree.color == 0 ? "red" : "black");
}
preorder(tree.left, tree.val, -1);
preorder(tree.right, tree.val, 1);
}
}