一、定义: 一种自平衡二叉查找树,它可以在O(logn)时间内完成查找、插入和删除,这里的n是树中元素的数目。
二、性质:
- 节点是红色或黑色
- 根节点是黑色
- 所有叶子都是黑色(叶子是NIL节点)
- 每个红色节点必须有两个黑色的子节点。(或者说从每个叶子到根的所有路径上不能有两个连续的红色节点。)(或者说不存在两个相邻的红色节点,相邻指两个节点是父子关系。)(或者说红色节点的父节点和子节点均是黑色的。)
- 从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点
(一个推论:如果一个节点存在黑子节点,那么该节点一定有两个子节点)
三、旋转:
左旋:以某个节点作为支点(旋转节点),其右子节点变为旋转节点的父节点,右子节点的左子节点变为旋转节点的右节点右旋:以某个节点作为支点(旋转节点),其左子节点变为旋转节点的父节点,左子节点的右子节点变为旋转节点的左子节点
四、插入:
-
为了插入之后不破坏平衡(黑高),新插入的节点必须为红色。
-
当前红黑树为空树时,直接把插入节点设为根,并且将红色变为黑色即可。
-
插入节点的父节点为红色时:
- (1)叔叔节点为红色: 这种情况将父亲和叔叔节点变为黑色,爷爷节点变为红色,回溯到爷爷节点重复进行上述操作即可。
- (2)叔叔节点不存在或者为黑节点时, 此时可以分为四种:
- LL双红:父亲节点变黑,爷爷节点变红,对爷爷节点右旋。(RR双红旋转方向相反即可)
- LR双红:先对父亲节点左旋,然后转变为LL双红的情况同上述处理即可。
- RR双红:同LL双红处理,将右旋变为左旋即可。
- RL双红:先对父节点右旋,转变为RR双红的情况同上述处理即可。
- LL双红:父亲节点变黑,爷爷节点变红,对爷爷节点右旋。(RR双红旋转方向相反即可)
-
插入节点的父节点为黑色时:直接插入即可。
五、删除:删除流程见下图
总共可以分为下面A B C D四种情况:
- 上述中C和D最终都可以转化A和B,对于A可以直接删除,B的话又会分为下面四种情况:
(注意:在上述第(1)种情况中,指针回溯遇到根节点和红节点时直接变黑就结束了,否则就继续递归)
(注意:在上述第(4)种情况中,经过变换调整后需要继续对当前要删除的节点循环调整)
六、手写红黑树源码:
import java.util.ArrayList;
import java.util.List;
public class RBtree<K extends Comparable<K>, V> {
private static final boolean RED = true;
private static final boolean BLACK = false;
private RBNode<K, V> root;
static class RBNode<K extends Comparable<K>, V> {
private boolean color;
private RBNode<K, V> parent;
private RBNode<K, V> left;
private RBNode<K, V> right;
private K key;
private V value;
public RBNode() {
}
public RBNode(boolean color, RBNode<K, V> parent, RBNode<K, V> left, RBNode<K, V> right, K key, V value) {
this.color = color;
this.parent = parent;
this.left = left;
this.right = right;
this.key = key;
this.value = value;
}
public boolean getColor() {
return color;
}
public void setColor(boolean color) {
this.color = color;
}
public RBNode<K, V> getParent() {
return parent;
}
public void setParent(RBNode<K, V> parent) {
this.parent = parent;
}
public RBNode<K, V> getLeft() {
return left;
}
public void setLeft(RBNode<K, V> left) {
this.left = left;
}
public RBNode<K, V> getRight() {
return right;
}
public void setRight(RBNode<K, V> right) {
this.right = right;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
}
/**
* 获取当前节点的父节点
*
* @param node
* @return
*/
private RBNode<K, V> parentOf(RBNode<K, V> node) {
if (node != null) return node.parent;
return null;
}
private boolean isRed(RBNode<K, V> node) {
if (node != null) {
return node.color == RED;
}
return false;
}
private boolean isBlack(RBNode<K, V> node) {
if (node != null) {
return node.color == BLACK;
}
return true; // null节点视为黑色
}
private void setRed(RBNode<K, V> node) {
if (node != null) {
node.setColor(RED);
}
}
private void setBlack(RBNode<K, V> node) {
if (node != null) {
node.setColor(BLACK);
}
}
public void inOrderPrint() {
inOrderPrint(this.root);
}
/**
* 中序打印
*
* @param node
*/
private void inOrderPrint(RBNode<K, V> node) {
if (node != null) {
inOrderPrint(node.left);
System.out.println("key:" + node.key + ",value:" + node.value);
inOrderPrint(node.right);
}
}
public RBNode<K, V> getRoot() {
return root;
}
/**
* 左旋
* p p
* | |
* x y
* / \ -----> / \
* lx y x ry
* / \ / \
* ly ry lx ly
*/
private void leftrotate(RBNode<K, V> x) {
if (x == null) return;
RBNode<K, V> y = x.right;
if (y == null) return;
//1、将x的右节点指向y的左子节点ly,将ly的父节点指向x
x.right = y.left;
if (y.left != null) {
y.left.parent = x;
}
// 2、将x和其父节点p的关系变为y和x的父节点的关系
y.parent = x.parent;
if (x.parent == null) {
this.root = y;
} else {
if (x.parent.left == x) {
x.parent.left = y;
} else {
x.parent.right = y;
}
}
//3、将x的父节点指向y,y的左节点指向x
x.parent = y;
y.left = x;
}
/**
* 右旋
*/
private void rightrotate(RBNode<K, V> x) {
if (x == null) return;
RBNode<K, V> y = x.left;
if (y == null) return;
x.left = y.right;
if (y.right != null) {
y.right.parent = x;
}
y.parent = x.parent;
if (x.parent == null) {
this.root = y;
} else {
if (x.parent.left == x) {
x.parent.left = y;
} else {
x.parent.right = y;
}
}
x.parent = y;
y.right = x;
}
/**
* 对外公开的插入方法
*/
public void insert(K key, V value) {
RBNode<K, V> node = new RBNode<>();
node.setKey(key);
node.setValue(value);
//新节点必须设为红色
node.setColor(RED);
insert(node);
}
private void insert(RBNode<K, V> node) {
if (this.root == null) {//如果为空树,则直接将插入的节点变为根节点,并将颜色变为黑色
node.setColor(BLACK);
this.root = node;
return;
}
RBNode<K, V> x = this.root;
RBNode<K, V> parent = x;
while (x != null) {
parent = x;
//cmp > 0 ,说明key大于 x.key 需要到x的右子树查找
//cmp < 0 ,说明key小于 x.key 需要到x的左子树查找
//cmp == 0,说明key等于 x.需要进行替换操作
int cmp = node.key.compareTo(x.key);
if (cmp > 0) {
x = x.right;
} else if (cmp < 0) {
x = x.left;
} else {
x.value = node.value;
return;//找到相同key时应该return
}
}
node.parent = parent;
int cmp = node.key.compareTo(parent.key);
if (cmp > 0) {
parent.right = node;
} else {
parent.left = node;
}
//插入有可能破坏平衡,下面进行修复平衡
insertFixup(node);
}
/**
* 修复由于插入导致的不平衡
*
* @param node
*/
private void insertFixup(RBNode<K, V> node) {
if (node == null) return;
RBNode<K, V> parent = parentOf(node);
if (parent == null || root == node) {//当前节点为根节点,直接设置为黑色即可
node.color = BLACK;
return;
}
//父亲节点为黑节点时不需要处理
if (isBlack(parent)) return;
RBNode<K, V> grandF = parentOf(parent);
if (grandF == null) return;
//父亲叔叔都为红色,父叔变黑,爷爷变红,回溯到爷爷节点继续上述处理
if (isRed(grandF.right) && isRed(grandF.left)) {
setBlack(grandF.left);
setBlack(grandF.right);
setRed(grandF);
insertFixup(grandF);
return;
}
//叔叔节点不存在或者为黑节点时,分为四种情况:
if (grandF.right == null || isBlack(grandF.right)) {
if (parent.left == node) {
//LL双红:父节点变黑,爷节点变红,爷节点右旋
setBlack(parent);
setRed(grandF);
rightrotate(grandF);
} else {
//LR双红:先对父节点左旋,然后再按照LL双红处理
leftrotate(parent);
setBlack(node);
setRed(grandF);
rightrotate(grandF);
}
} else if (grandF.left == null || isBlack(grandF.left)) {
if (parent.right == node) {
//RR双红 :父节点变黑,爷节点变红,爷节点左旋
setBlack(parent);
setRed(grandF);
leftrotate(grandF);
} else {
//RL双红:先对父节点右旋,然后再按照RR双红处理
rightrotate(parent);
setBlack(node);
setRed(grandF);
leftrotate(grandF);
}
}
}
/**
* 找结点(x)的后继结点。即,查找"红黑树中数据值大于该结点"的"最小结点"。
*/
public RBNode<K, V> findSuccessor(RBNode<K, V> node) {
if (node.right != null) {
return findMin(node.right);
} else {
RBNode<K, V> parent = node.parent;
while (parent != null && node == parent.right) {
node = parent;
parent = parent.parent;
}
return parent;
}
}
/**
* 找结点(x)的前驱结点。即,查找"红黑树中数据值小于该结点"的"最小结点"。
*/
public RBNode<K, V> findPredecessor(RBNode<K, V> node) {
if (node.left != null) {
return findMax(node.left);
} else {
RBNode<K, V> parent = node.parent;
while (parent != null && node == parent.left) {
node = parent;
parent = parent.parent;
}
return parent;
}
}
/**
* 查找最小节点
*
* @param node
* @return
*/
private RBNode<K, V> findMin(RBNode<K, V> node) {
if (node == null) return null;
while (node.left != null) {
node = node.left;
}
return node;
}
/**
* 查找最大节点
*
* @param node
* @return
*/
private RBNode<K, V> findMax(RBNode<K, V> node) {
if (node == null) return null;
while (node.right != null) {
node = node.right;
}
return node;
}
/**
* 对外公开的删除方法
*/
public void deleteNode(K key) {
deleteNode(root, key);
}
private void deleteNode(RBNode<K, V> root, K key) {
RBNode<K, V> node = findNodeRec(root, key);
if (node == null) return;
if (node.left != null && node.right != null) {//有两个子节点
RBNode<K, V> successorNode = findSuccessor(node);
node.key = successorNode.key;
node.value = successorNode.value;
node = successorNode;
}
if (node.left == null && node.right == null) {//没有子节点
if (node.color == RED) {
clearNode(node);
} else {
if (node.parent == null) {
node = null;
this.root = node;
} else {
//黑节点平衡处理
deleteFix(node);
clearNode(node);
}
}
} else {//只有一个子节点
RBNode<K, V> child = node.left != null ? node.left : node.right;
child.color = BLACK;
if (node.parent == null) {//说明node节点是根节点
this.root = child;
child.parent = null;
} else {
child.parent = node.parent;
if (node == node.parent.left) {
node.parent.left = child;
} else {
node.parent.right = child;
}
}
}
}
/**
* 删除后平衡操作
*
* @param node
*/
private void deleteFix(RBNode<K, V> node) {
RBNode<K, V> pp = parentOf(node);
if (pp == null) return; //根节点,无需处理
if (node == pp.left) {
RBNode<K, V> ppr = pp.right;
//注意这里ppr可能为null
if (ppr != null && ppr.color == RED) { /** (4) **/
//兄弟为红则:父变红,兄弟变黑,左旋父,继续递归
pp.color = RED;
ppr.color = BLACK;
leftrotate(pp);
deleteFix(node);
} else if (ppr.right != null && ppr.right.color == RED) { /** (2) **/
//兄弟右孩子(远端)为红:(兄弟)以父为名,父亲变黑,孩子变黑,左旋父
ppr.color = pp.color;
pp.color = BLACK;
ppr.right.color = BLACK;
leftrotate(pp);
} else if (ppr.left != null && ppr.left.color == RED) { /** (3) **/
//兄弟左孩子为红:兄弟变红,兄弟孩子变黑,右旋兄弟,回到情况(2)
ppr.color = RED;
ppr.left.color = BLACK;
rightrotate(ppr);
//-----回到情况(2)---------------------------
ppr = pp.right;//(注意:旋转后需要更新ppr)
ppr.color = pp.color;
pp.color = BLACK;
ppr.right.color = BLACK;
leftrotate(pp);
} else { /** (1) **/
//兄弟和兄弟子节点全部为黑:兄弟变红,指针回溯到父亲节点继续递归(节点上移,如果碰到根节点直接停止,如果碰到红节点则变黑即可)
ppr.color = RED;
if (parentOf(pp) != null && pp != root) {
if (pp.color == RED) {//碰到红节点,直接变黑
pp.color = BLACK;
} else {
deleteFix(pp);//继续递归
}
} else {
//碰到根节点,直接停止即可
}
}
} else {
RBNode<K, V> ppl = pp.left;
if (ppl.color == RED) { /** (4) **/
//兄弟为红则:父变红,兄弟变黑,右旋父,继续递归
pp.color = RED;
ppl.color = BLACK;
rightrotate(pp);
deleteFix(node);
} else if (ppl.left != null && ppl.left.color == RED) { /** (2) **/
//兄弟左孩子(远端)为红:(兄弟)以父为名,父亲变黑,孩子变黑,右旋父
ppl.color = pp.color;
pp.color = BLACK;
ppl.left.color = BLACK;
rightrotate(pp);
} else if (ppl.right != null && ppl.right.color == RED) { /** (3) **/
//兄弟右孩子为红:兄弟变红,兄弟孩子变黑,左旋兄弟,回到情况(2)
ppl.color = RED;
ppl.right.color = BLACK;
leftrotate(ppl);
//-----回到情况(2)---------------------------
ppl = pp.left;//(注意:旋转后需要更新ppl)
ppl.color = pp.color;
pp.color = BLACK;
ppl.left.color = BLACK;
rightrotate(pp);
} else { /** (1) **/
//兄弟和兄弟子节点全部为黑:兄弟变红,指针回溯到父亲节点继续递归(节点上移,如果碰到根节点直接停止,如果碰到红节点则变黑即可)
ppl.color = RED;
if (pp.parent != null && pp != root) {
if (pp.color == RED) {//碰到红节点,直接变黑
pp.color = BLACK;
} else {
deleteFix(pp);//继续递归
}
} else {
//碰到根节点,直接停止即可
}
}
}
}
/**
* 删除当前节点
*
* @param node
*/
private void clearNode(RBNode<K, V> node) {
RBNode<K, V> parent = node.parent;
if (parent != null) {
if (node == parent.left) {
parent.left = null;
} else {
parent.right = null;
}
} else {
// 如果是根节点,需要更新root引用
this.root = null;
}
// 清理节点自身的引用,避免内存泄漏
node.parent = null;
node.left = null;
node.right = null;
node.key = null;
node.value = null;
}
/**
* 查找指定节点
*
* @param root
* @param key
* @return
*/
private RBNode<K, V> findNodeRec(RBNode<K, V> root, K key) {
if (root == null || key == null) return null;
int cmp = key.compareTo(root.key);
if (cmp < 0) {
return findNodeRec(root.left, key);
} else if (cmp > 0) {
return findNodeRec(root.right, key);
} else {
return root;
}
}
//---------------------------------------------------------------------------------------------------------------------------
/**
* 下面是辅助打印的相关代码,可以不用管
**/
//---------------------------------------------------------------------------------------------------------------------------
public void printTree(RBtree<K, V> tree) {
RBNode<K, V> root = tree.getRoot();
if (root == null) {
System.out.println("Tree is empty");
return;
}
List<List<String>> lines = new ArrayList<>();
List<RBNode<K, V>> level = new ArrayList<>();
List<RBNode<K, V>> next = new ArrayList<>();
level.add(root);
int nn = 1;
int widest = 0;
while (nn != 0) {
List<String> line = new ArrayList<>();
nn = 0;
for (RBNode<K, V> n : level) {
if (n == null) {
line.add(null);
next.add(null);
next.add(null);
} else {
String aa = n.key + "(" + (n.color == RED ? "R" : "B") + ")";
line.add(aa);
if (aa.length() > widest) widest = aa.length();
next.add(n.left);
next.add(n.right);
if (n.left != null) nn++;
if (n.right != null) nn++;
}
}
if (widest % 2 == 1) widest++;
lines.add(line);
List<RBNode<K, V>> tmp = level;
level = next;
next = tmp;
next.clear();
}
int perPiece = lines.get(lines.size() - 1).size() * (widest + 4);
for (int i = 0; i < lines.size(); i++) {
List<String> line = lines.get(i);
int hpw = (int) Math.floor(perPiece / 2f) - 1;
if (i > 0) {
for (int j = 0; j < line.size(); j++) {
// split node
char c = ' ';
if (j % 2 == 1) {
if (line.get(j - 1) != null) {
c = (line.get(j) != null) ? '┴' : '┘';
} else {
if (j < line.size() && line.get(j) != null) c = '└';
}
}
System.out.print(c);
// lines and spaces
if (line.get(j) == null) {
for (int k = 0; k < perPiece - 1; k++) {
System.out.print(" ");
}
} else {
for (int k = 0; k < hpw; k++) {
System.out.print(j % 2 == 0 ? " " : "─");
}
System.out.print(j % 2 == 0 ? "┌" : "┐");
for (int k = 0; k < hpw; k++) {
System.out.print(j % 2 == 0 ? "─" : " ");
}
}
}
System.out.println();
}
for (int j = 0; j < line.size(); j++) {
String f = line.get(j);
if (f == null) f = "";
int gap1 = (int) Math.ceil(perPiece / 2f - f.length() / 2f);
int gap2 = (int) Math.floor(perPiece / 2f - f.length() / 2f);
// a node
for (int k = 0; k < gap1; k++) {
System.out.print(" ");
}
System.out.print(f);
for (int k = 0; k < gap2; k++) {
System.out.print(" ");
}
}
System.out.println();
perPiece /= 2;
}
}
}