码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2014-06-07 13:09:28      阅读:215      评论:0      收藏:0      [点我收藏+]

标签:红黑树   java红黑树   

---------------------- 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

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

标签:红黑树   java红黑树   

原文地址:http://blog.csdn.net/u013765450/article/details/28978129

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!