马程序员学习笔记——红黑树解析四

来源:互联网 发布:nginx proxy temp 编辑:程序博客网 时间:2024/04/28 00:34

---------------------- 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]] ← y10             else right[p[x]] ← y11  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] ← z11     else if key[z] < key[y]12             then left[y] ← z13             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 211                                LEFT-ROTATE(T, z)              ▹ Case 212                           color[p[z]] ← BLACK                 ▹ Case 313                           color[p[p[z]]] ← RED                ▹ Case 314                           RIGHT-ROTATE(T, p[p[z]])            ▹ Case 315           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] ← x10    else if y = left[p[y]]11            then left[p[y]] ← x12            else right[p[y]] ← x13 if y 3≠ z14    then key[z] ← key[y]15         copy y's satellite data into z16 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]] = BLACK10                   then color[w] ← RED                          ▹  Case 211                        x ← p[x]                                  ▹  Case 212                   else if color[right[w]] = BLACK13                           then color[left[w]] ← BLACK          ▹  Case 314                                color[w] ← RED                  ▹  Case 315                                RIGHT-ROTATE(T, w)              ▹  Case 316                                w ← right[p[x]]                 ▹  Case 317                         color[w] ← color[p[x]]                 ▹  Case 418                         color[p[x]] ← BLACK                    ▹  Case 419                         color[right[w]] ← BLACK                ▹  Case 420                         LEFT-ROTATE(T, p[x])                   ▹  Case 421                         x ← root[T]                            ▹  Case 422        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;elsex.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;elsex.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;elsez.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;elsez.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;elsez.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;} elsereturn 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

2 0