---------------------- 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
马程序员学习笔记——红黑树解析四,布布扣,bubuko.com
原文地址:http://blog.csdn.net/u013765450/article/details/28978129