---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
本篇是将上面三篇的理论知识转化成代码,java实现
首先,看一下算法导论里的伪代码
一、左旋
The pseudocode for LEFT-ROTATE assumes that right[x] ≠ nil[T] and that the root's parent is nil[T].(伪代码的左旋方法中假设X的右孩子不为空)
LEFT-ROTATE(T, x)
1 y ← right[x] ▹ Set y.
2 right[x] ← left[y] //开始变化,y的左孩子成为x的右孩子
3 if left[y] !=nil[T]
4 then p[left[y]] <- x
5 p[y] <- p[x] //y成为x的父结点
6 if p[x] = nil[T]
7 then root[T] <- y
8 else if x = left[p[x]]
9 then left[p[x]] ← y
10 else right[p[x]] ← y
11 left[y] ← x //x成为y的左孩子(一月三日修正)
12 p[x] ← y
二、元素插入
RB-INSERT(T, z) //注意我给的注释...
1 y ← nil[T] // y 始终指向 x 的父结点。
2 x ← root[T] // x 指向当前树的根结点,
3 while x ≠ nil[T]
4 do y ← x
5 if key[z] < key[x] //向左,向右..
6 then x ← left[x]
7 else x ← right[x] // 为了找到合适的插入点,x 探路跟踪路径,直到x成为NIL 为止。
8 p[z] ← y // y置为 插入结点z 的父结点。
9 if y = nil[T]
10 then root[T] ← z
11 else if key[z] < key[y]
12 then left[y] ← z
13 else right[y] ← z //此 8-13行,置z 相关的指针。
14 left[z] ← nil[T]
15 right[z] ← nil[T] //设为空,
16 color[z] ← RED //将新插入的结点z作为红色
17 RB-INSERT-FIXUP(T, z) //因为将z着为红色,可能会违反某一红黑性质,
//所以需要调用RB-INSERT-FIXUP(T, z)来保持红黑性质。
三、插入修复
RB-INSERT-FIXUP(T, z)
1 while color[p[z]] = RED
2 do if p[z] = left[p[p[z]]]
3 then y ← right[p[p[z]]]
4 if color[y] = RED
5 then color[p[z]] ← BLACK ▹ Case 1
6 color[y] ← BLACK ▹ Case 1
7 color[p[p[z]]] ← RED ▹ Case 1
8 z ← p[p[z]] ▹ Case 1
9 else if z = right[p[z]]
10 then z ← p[z] ▹ Case 2
11 LEFT-ROTATE(T, z) ▹ Case 2
12 color[p[z]] ← BLACK ▹ Case 3
13 color[p[p[z]]] ← RED ▹ Case 3
14 RIGHT-ROTATE(T, p[p[z]]) ▹ Case 3
15 else (same as then clause
with "right" and "left" exchanged)
16 color[root[T]] ← BLACK
四、删除
RB-DELETE(T, z)
1 if left[z] = nil[T] or right[z] = nil[T]
2 then y ← z
3 else y ← TREE-SUCCESSOR(z)
4 if left[y] ≠ nil[T]
5 then x ← left[y]
6 else x ← right[y]
7 p[x] ← p[y]
8 if p[y] = nil[T]
9 then root[T] ← x
10 else if y = left[p[y]]
11 then left[p[y]] ← x
12 else right[p[y]] ← x
13 if y 3≠ z
14 then key[z] ← key[y]
15 copy y's satellite data into z
16 if color[y] = BLACK //如果y是黑色的,
17 then RB-DELETE-FIXUP(T, x) //则调用RB-DELETE-FIXUP(T, x)
18 return y //如果y不是黑色,是红色的,则当y被删除时,红黑性质仍然得以保持。不做操作,返回。
//因为:1.树种各结点的黑高度都没有变化。2.不存在俩个相邻的红色结点。
//3.因为入宫y是红色的,就不可能是根。所以,根仍然是黑色的。
五、删除修复
RB-DELETE-FIXUP(T, x)
1 while x ≠ root[T] and color[x] = BLACK
2 do if x = left[p[x]]
3 then w ← right[p[x]]
4 if color[w] = RED
5 then color[w] ← BLACK ▹ Case 1
6 color[p[x]] ← RED ▹ Case 1
7 LEFT-ROTATE(T, p[x]) ▹ Case 1
8 w ← right[p[x]] ▹ Case 1
9 if color[left[w]] = BLACK and color[right[w]] = BLACK
10 then color[w] ← RED ▹ Case 2
11 x ← p[x] ▹ Case 2
12 else if color[right[w]] = BLACK
13 then color[left[w]] ← BLACK ▹ Case 3
14 color[w] ← RED ▹ Case 3
15 RIGHT-ROTATE(T, w) ▹ Case 3
16 w ← right[p[x]] ▹ Case 3
17 color[w] ← color[p[x]] ▹ Case 4
18 color[p[x]] ← BLACK ▹ Case 4
19 color[right[w]] ← BLACK ▹ Case 4
20 LEFT-ROTATE(T, p[x]) ▹ Case 4
21 x ← root[T] ▹ Case 4
22 else (same as then clause with "right" and "left" exchanged)
23 color[x] ← BLACK
-------------------------------------------------------华丽分割线----------------------------------------------------------------------
六、红黑树的java实现
除了必备的左右旋、插入、删除,另外还增加了求前趋后继,中序遍历,最大值最小值等方法
代码:
class RBTree {
public static final boolean RED = false;
public static final boolean BLACK = true;
// 定义节点的结构
private class Node {
int value;
boolean color = false;
Node parent = null;
Node left = null;
Node right = null;
public Node() {
}
private Node(int value, boolean color, Node parent, Node left, Node right) {
this.value = value;
this.color = color;
this.parent = parent;
this.left = left;
this.right = right;
}
public String toString() {
// return ""+value;
String colour;
if (color == BLACK) {
colour = "黑";
} else {
colour = "红";
}
// return "[我是"+value+" :: 颜色="+colour+"]";
return "[我是"+value+" :: 颜色="+colour+" 上=" +parent.value+ ",左=" +left.value +",右="+right.value+"]";
}
}
// 定义nil节点和根节点
private final Node nil = new Node(-999, BLACK, null, null, null);
private Node root = nil;
public Node getRoot() {
return root;
}
// 对外提供的插入操作
// 在树中添加元素
public boolean put(int x) {
Node n = new Node(x, RED, nil, nil, nil);
return insert(n);
}
// 左旋
// 左旋
private void left_Rotate(Node x) {
Node y = x.right; // 记录x右孩子
x.right = y.left; // 将y的左孩子变成x的右孩子
if (y.left != nil) {
y.left.parent = x;
}
y.parent = x.parent; // y代替x的根节点位置
if (x.parent == nil) {
y.parent = nil;
root = y;
} else if (x == x.parent.left)
x.parent.left = y;
else
x.parent.right = y;
y.left = x; // x成为y的左孩子
x.parent = y;
}
// 右旋
// 右旋
private void right_Rotate(Node x) {
Node y = x.left; // 记录x左孩子
x.left = y.right; // 将y的右孩子变成x的左孩子
if (y.right != nil) {
y.right.parent = x;
}
y.parent = x.parent; // y代替x的根节点位置
if (x.parent == nil) {
y.parent = nil;
root = y;
} else if (x == x.parent.right)
x.parent.right = y;
else
x.parent.left = y;
y.right = x; // x成为y的左孩子
x.parent = y;
}
// 插入元素
// 插入元素
private boolean insert(Node z) {
Node x = root; //从根节点开始寻找合适插入点
Node y = nil; //y作为x的父亲
while (x != nil) {
if (z.value < x.value) {
y = x;
x = x.left;
} else if (z.value > x.value) {
y = x;
x = x.right;
} else {
return false; //已经有了相同点
}
}
z.parent = y;
if (y == nil) { //如果插入前还是空树
root = z;
z.color = BLACK;
return true;
} else if (z.value < y.value) //如果z比y小
y.left = z;
else //如果z比y大
y.right = z;
if (y.color == RED) { //如果插入点父亲是红色的,就需要修复红黑树,否则红黑树还是平衡的
insert_Fixed(z);
}
return true; //正常插入后就返回TRUE
}
// 修复因插入引起的失衡
// 修复插入引起的失衡
private void insert_Fixed(Node z) {
while (z.parent.color == RED) { //假如插入点z的父亲的颜色是红色,就继续循环
if (z.parent == z.parent.parent.left) { //如果插入点z是它爷爷的左分支,因为既然z的父亲是红色的,那么它肯定有爷爷
Node u = z.parent.parent.right; //记录z的叔叔
if (u.color == RED) { //case1 : 父亲和叔叔都是红的
z.parent.color = BLACK; //策略:上推一层红
u.color = BLACK;
z.parent.parent.color = RED;
z = z.parent.parent; //把爷爷作为新的插入点继续循环
} else {
if (z == z.parent.right) { //case2 : 父亲是红,叔叔黑,并且z是父亲的右儿子
z = z.parent; //策略:以它父亲为pivot,左旋
left_Rotate(z);
}
z.parent.color = BLACK; //case3: 父红叔黑,z是父的右儿子
z.parent.parent.color = RED;
right_Rotate(z.parent.parent);
}
} else { //如果插入点z是它爷爷的左分支,那么一切操作相反
Node u = z.parent.parent.left;
if (u.color == RED) {
z.parent.color = BLACK;
u.color = BLACK;
z.parent.parent.color = RED;
z = z.parent.parent;
} else {
if (z == z.parent.left) {
z = z.parent;
right_Rotate(z);
}
z.parent.color = BLACK;
z.parent.parent.color = RED;
left_Rotate(z.parent.parent);
}
}
}
//System.out.println("root = " + root);
root.color = BLACK;
}
// 删除元素
public int remove(int i) throws RuntimeException {
Node z = get(i);
if (z == null) { //如果树中没有数据就抛异常
throw new RuntimeException("无此数据");
}
// 如果没删节点有两个儿子,那么真正删除的是它的前趋或后继,这里我取的是后继,本质是一样的,有可能形式会不同
if (z.left != nil && z.right != nil) {
Node delete = successor(z);
z.value = delete.value;
z = delete;
}
Node replace = null;
// 如被删点没有儿子
if (z.left == nil && z.right == nil) {
// 被删点又是红色的,就需要修复
if (z.color == BLACK)
delete_Fixed(z);
// 修复完后真正删除
if (z.parent != nil) { //如果被删点不是根节点
if (z == z.parent.left)
z.parent.left = nil;
else
z.parent.right = nil;
} else { //被删点就是根节点,那就直接变空树了
root = nil;
}
return z.value;
// 现在只剩下只有一个儿子的情况了
} else if (z.left != nil) { //如果是左儿子
replace = z.left;
replace.parent = z.parent;
if (z.parent != nil) { //如果被删点不是根节点,那么把它父亲直接连到它儿子上
if (z == z.parent.left)
z.parent.left = replace;
else
z.parent.right = replace;
} else {
root = replace; //如果被删点是根节点,那么把它儿子作为新的根节点
}
} else { //如果是右儿子,操作相反
replace = z.right;
replace.parent = z.parent;
if (z.parent != nil) {
if (z == z.parent.left)
z.parent.left = replace;
else
z.parent.right = replace;
} else {
root = replace;
}
}
// 如果被删点是黑色就需要修复
if (z.color == BLACK) {
delete_Fixed(replace);
}
return z.value;
}
// 修复因删除引起的失衡
public void delete_Fixed(Node z) {
// 如果被删点不是根节点,并且颜色是黑色
while (z != root && z.color == BLACK) {
// 如果被删点是它父亲左分支
if (z == z.parent.left) {
Node sibling = z.parent.right; //记录z的兄弟
if (sibling.color == RED) { //case1 : 如果他兄弟是红色的
sibling.color = BLACK; //对策:父亲变红,兄弟变黑,以父亲为支点左旋
z.parent.color = RED;
left_Rotate(z.parent);
sibling = z.parent.right;
}
if (sibling.left.color == BLACK && sibling.right.color == BLACK) { //case2 : 兄黑,两侄子黑
sibling.color = RED; //对策,兄变红,然后以父亲为新的判定点
z = z.parent;
} else if (sibling.right.color == BLACK) { //case3 : 兄黑,左侄红,右侄黑
sibling.left.color = BLACK; //对策:兄变红,左侄变黑,以兄为支点右旋
sibling.color = RED;
right_Rotate(sibling);
sibling = z.parent.right;
}
if (sibling.right.color == RED) { //case4 :兄黑,右侄红,左侄随意
sibling.color = z.parent.color; //对策,兄变父色,父变黑,右侄变黑,以父为支点左旋
z.parent.color = BLACK;
sibling.right.color = BLACK;
left_Rotate(z.parent);
z.color = RED; //这句只是为了退出循环
}
}
// 如果被删点是它父亲右分支,一切操作相反
else {
Node sibling = z.parent.left;
if (sibling.color == RED) {
sibling.color = BLACK;
z.parent.color = RED;
right_Rotate(z.parent);
sibling = z.parent.left;
}
if (sibling.left.color == BLACK && sibling.right.color == BLACK) {
sibling.color = RED;
z = z.parent;
} else if (sibling.left.color == BLACK) {
sibling.right.color = BLACK;
sibling.color = RED;
left_Rotate(sibling);
sibling = z.parent.left;
}
if (sibling.left.color == RED) {
sibling.color = z.parent.color;
z.parent.color = BLACK;
sibling.left.color = BLACK;
right_Rotate(z.parent);
z.color = RED; //这句只是为了退出循环
}
}
}
z.color = BLACK;
// root.color = BLACK;
}
// 求某个节点的前趋
// 求某个节点的中序前趋
private Node predecessor(Node z) {
if (z == nil) //空节点没有前趋
return nil;
Node x = z.left;
Node y = z; //y作为x的父亲
if (x == nil) { //z没有左分支,那么向上追溯,直到找到分支为右分支的那个节点作为前趋
do {
x = y;
y = x.parent;
} while (y != nil && x == y.left);
return y;
}
while (x != nil) {
y = x;
x = x.right;
}
return y;
}
// 求某个节点的后继
// 求某个节点的中序后继
private Node successor(Node z) {
if (z == nil) //空节点没有后继
return nil;
Node x = z.right;
Node y = z; //y作为x的父亲
if (x == nil) { //z没有右分支,那么向上追溯,直到找到分支为左分支的那个节点作为前趋
do {
x = y;
y = x.parent;
} while (y != nil && x == y.right);
return y;
}
while (x != nil) {
y = x;
x = x.left;
}
return y;
}
// 中序遍历(从小到大)
public void inorder() {
Node start = getMinNode();
System.out.print(start.value + " ");
while ((start = successor(start)) != nil) {
System.out.print(start.value + " ");
}
}
// 获取最小节点
private Node getMinNode() {
Node min = root;
Node y = min.parent; //y指向最小值的父亲
while (min != nil) {
y = min;
min = min.left;
}
return y;
}
// 获取最大节点
private Node getMaxNode() {
Node max = root;
Node y = max.parent; //y指向最大值的父亲
while (max != nil) {
y = max;
max = max.right;
}
return y;
}
// 寻找某个节点
public Node get(int i) {
Node z = new Node(i, BLACK, nil, nil, nil);
Node result = getNode(z);
if (result == nil) {
return null;
} else {
return result;
}
}
// 寻找某个节点
public Node getNode(Node z) {
Node x = root;
while (x != nil) {
if (z.value < x.value) {
x = x.left;
} else if (z.value > x.value){
x = x.right;
} else
return x;
}
return nil; //如果没找到,或者是空树,就返回NULL
}
}
测试代码:
public static void main(String[] args) {
RBTree tree = new RBTree();
tree.put(12);
tree.put(1);
tree.put(9);
tree.put(2);
tree.put(0);
tree.put(11);
tree.put(7);
tree.put(19);
tree.put(4);
tree.put(15);
tree.put(18);
tree.put(5);
tree.put(14);
tree.put(13);
tree.put(10);
tree.put(16);
tree.put(6);
tree.put(3);
tree.put(8);
tree.put(17);
System.out.println();
tree.remove(12);
tree.remove(1);
tree.remove(9);
// tree.remove(2);
// tree.remove(0);
// tree.remove(11);
// tree.remove(7);
// tree.remove(19);
// tree.remove(4);
// tree.remove(15);
// tree.remove(18);
// tree.remove(5);
// tree.remove(14);
// tree.remove(13);
// tree.remove(10);
// tree.remove(16);
// tree.remove(6);
// tree.remove(3);
// tree.remove(8);
// tree.remove(17);
System.out.println("gen " + tree.getRoot());
System.out.println();
System.out.println(tree.get(12));
System.out.println(tree.get(1));
System.out.println(tree.get(9));
System.out.println(tree.get(2));
System.out.println(tree.get(0));
System.out.println(tree.get(11));
System.out.println(tree.get(7));
System.out.println(tree.get(19));
System.out.println(tree.get(4));
System.out.println(tree.get(15));
System.out.println(tree.get(18));
System.out.println(tree.get(5));
System.out.println(tree.get(14));
System.out.println(tree.get(13));
System.out.println(tree.get(10));
System.out.println(tree.get(16));
System.out.println(tree.get(6));
System.out.println(tree.get(3));
System.out.println(tree.get(8));
System.out.println(tree.get(17));
tree.inorder();
参考:
经典算法研究系列:五、红黑树算法的实现与剖析
http://blog.csdn.net/v_JULY_v/article/details/6109153
红黑树从头至尾插入和删除结点的全程演示图
http://blog.csdn.net/v_JULY_v/article/details/6284050
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------详细请查看:http://edu.csdn.net