1. 程式人生 > >RBT紅黑樹-JAVA版本

RBT紅黑樹-JAVA版本

這個玩意程式碼量巨大,模仿著別人寫了整整一天...

Java因為沒有引用傳遞,所以構建樹要麼是全域性設定根然後更改,要麼函式返回的是根.....

紅黑樹確保沒有一條路徑比其他的路徑長出2倍左右,因而是接近平衡的

1. 紅黑樹性質(限制):

1)每個結點要麼是紅的要麼是黑的。  
2)根結點是黑的。  
3)每個葉結點(葉結點即指樹尾端NIL指標或NULL結點)都是黑的。  
4)如果一個結點是紅的,那麼它的兩個兒子都是黑的。  
5)對於任意結點而言,其到葉結點樹尾端NIL指標的每條路徑都包含相同數目的黑結點。

2. 紅黑樹的效能分析:

挖坑,明天寫.

3.預備知識:

左旋(右旋)指的是將這個點的右(左)子樹佔據自己的位置,原來的點變為其左(右)子樹.


4.核心操作:

1) 插入

和AVL差不多,就是有個調整顏色的過程

	/* 
	 * 如果父節點是黑色就都沒違反不用調整.
	 * 反之....
	 * 插入一個結點時。可能被破壞的性質為(4)如果一個結點是紅色的,則它的孩子結點是黑色的   (2) 根結點是黑色的 
	 * 插入修復情況1:如果當前結點的父結點是紅色且祖父結點的另一個子結點(叔叔結點)是紅色
         * 插入修復情況2:當前節點的父節點是紅色,叔叔節點是黑色,當前節點是其父節點的右子
 	 * 插入修復情況3:當前節點的父節點是紅色,叔叔節點是黑色,當前節點是其父節點的左子
	*/


2)刪除

比較複雜,四種情況...

/* 
	* 如果刪除的是紅色點,沒影響........
	* 如果刪除的是黑色點,代替它位置的有紅/黑兩種情況,
	* 1. 紅,直接將這個點改成黑色
	* 2.1 黑且是根節點什麼都不用做
	* 2.2 刪除修復情況1,當前節點顏色是黑,兄弟節點為紅色(此時父節點和兄弟節點的子節點分為黑)
	* 2.2刪除修復情況2,當前節點顏色是黑,兄弟是黑色且兄弟節點的兩個子節點全為黑色
	* 2.2刪除修復情況3,當前節點顏色是黑,兄弟節點是黑色,兄弟的左子是紅色,右子是黑色
	* 2.2刪除修復情況4,當前節點顏色是黑,兄弟節點是黑色,但是兄弟節點的右子是紅色,
*/

程式碼:
import java.util.Arrays;
import java.util.Collection;
import java.util.Scanner;
interface ANode{
	public void rbInorderTravel(Node node);
	public Node rbSearch(Node node);
	public Node rbMinNode(Node node);
	public Node rbMaxNode(Node node);
	public boolean rbInsert(Node node,int data);
	public boolean rbDelete(Node node,int data);
	public void LL(Node node);
	public void RR(Node node);
	
}
public class Main {
	
	public static void main(String[] args) {
		int [] a = new int[20];
		for(int i = 0;i<20;i++){
			a[i] = (int) (Math.random()*1000);
			System.out.print(a[i] + " ");
			Node.rbInsert(Node.root, a[i]);
		}System.out.println();
		Node.rbInorderTravel(Node.root);
		Node.rbDelete(Node.root, a[1]);
		Node.rbDelete(Node.root, a[9]);
		Node.rbDelete(Node.root, a[0]);
		Node.rbInorderTravel(Node.root);
	}
}

class Node{
	public static Node root = null;
	public static int RED = 1;
	public static int BLACK = 2;
	Node lson,parent,rson;
	int high;
	int data;
	int color;
	public Node() {
		super();
		lson = rson = null;
		high = 0;
		this.color = RED;
	}

	public Node(int data) {
		// TODO Auto-generated constructor stub
		this();
		this.data = data;
	}
	public void free(){
		this.lson = this.rson = null;
		this.parent = null;
	}
	public static Node rbSearch(Node node,int data){
		while(node != null){
			if(data < node.data)
				node = node.lson;
			else if(data > node.data)
				node = node.rson;
			else return node;
		}
		return null;
	}
	public static Node rbSuccessor(Node node){
		Node pre = null;
		while(node != null){
			pre = node;
			node = node.lson;
		}
		return pre;
	}

	public static void rbInorderTravel(Node node) {
		// TODO Auto-generated method stub
		if(null == node)
			return;
		rbInorderTravel(node.lson);
		System.out.print(node.data+" ");
		rbInorderTravel(node.rson);
		if(node == root)
			System.out.println();
	}

	public static boolean rbInsert(Node node, int data) {
		// TODO Auto-generated method stub
		Node now = new Node(data);
		Node pre = null;
		while(null != node){
			pre = node;
			if(data < node.data)
				node = node.lson;
			else node = node.rson;
		}
		if(pre == null)
			root = now;
		else{
			if(data < pre.data)
				pre.lson = now;
			else pre.rson = now;
		}
		now.parent = pre;
		rbTreeInsertFixup(now);
		return true;
	}
	/* 
	 * 如果父節點是黑色就都沒違反不用調整.
	 * 反之....
	 * 插入一個結點時。可能被破壞的性質為(4)如果一個結點是紅色的,則它的孩子結點是黑色的   (2) 根結點是黑色的 
	 * 插入修復情況1:如果當前結點的父結點是紅色且祖父結點的另一個子結點(叔叔結點)是紅色
     * 插入修復情況2:當前節點的父節點是紅色,叔叔節點是黑色,當前節點是其父節點的右子
 	 * 插入修復情況3:當前節點的父節點是紅色,叔叔節點是黑色,當前節點是其父節點的左子
	*/
	private static void rbTreeInsertFixup(Node node) {
		// TODO Auto-generated method stub
		Node uncle,gparent,p;
		while((p=node.parent) != null && p.color ==RED){
			gparent = p.parent;
			//如果父結點是祖父結點的左孩子(因為父結點是紅色結點,所以肯定有祖父結點)  
			if(p == gparent.lson){
				uncle = gparent.rson;
				if(uncle != null && uncle.color == RED){//修復情況1
					gparent.color = RED;
					p.color = BLACK;
					uncle.color = BLACK;
					node = gparent;
				}else{  //叔父不存在或者存在但是顏色是黑色的,則必須通過尋轉來配合改變顏色來保持性質2
					if(node == p.rson){//修復情況2
						node = p;
						LL(node);
						p = node.parent;
					}// 情況2:x為其父結點的右孩子,通過左旋轉換為情況3  
					//情況三:x為其父結點的左孩子,調整父結點和祖父結點的顏色,以糾正性質4,但是破壞了性質5  
					p.color = BLACK;
					gparent.color = RED;
					RR(gparent);//此時x->parent->color = BLACK, 迴圈結束
				}
			}else{
				uncle = gparent.lson;
				if(uncle != null && uncle.color == RED){
					gparent.color = RED;
					p.color = BLACK;
					uncle.color = BLACK;
					node = gparent;
				}else{  //叔父不存在或者存在但是顏色是黑色的,則必須通過尋轉來配合改變顏色來保持性質2
					if(node == p.lson){
						node = p;
						RR(node);
						p = node.parent;
					}// 情況2:x為其父結點的右孩子,通過左旋轉換為情況3  
					//情況三:x為其父結點的左孩子,調整父結點和祖父結點的顏色,以糾正性質4,但是破壞了性質5  
					p.color = BLACK;
					gparent.color = RED;
					LL(gparent);//此時x->parent->color = BLACK, 迴圈結束
				}
			}
		}
		root.color = BLACK;//保持性質2,根為黑色
	}
	  
	/* 
	* 如果刪除的是紅色點,沒影響........
	* 如果刪除的是黑色點,代替它位置的有紅/黑兩種情況,
	* 1. 紅,直接將這個點改成黑色
	* 2.1 黑且是根節點什麼都不用做
	* 2.2 刪除修復情況1,當前節點顏色是黑,兄弟節點為紅色(此時父節點和兄弟節點的子節點分為黑)
	* 2.2刪除修復情況2,當前節點顏色是黑,兄弟是黑色且兄弟節點的兩個子節點全為黑色
	* 2.2刪除修復情況3,當前節點顏色是黑,兄弟節點是黑色,兄弟的左子是紅色,右子是黑色
	* 2.2刪除修復情況4,當前節點顏色是黑,兄弟節點是黑色,但是兄弟節點的右子是紅色,
	* 
	* 另一種解釋:
	* 刪除一個黑結點會導致如下三個問題:  
	* (1)如果被刪除結點y是根結點,而y的一個紅色孩子成為了新的根,則違反了性質2 
	* (2)如何y的父結點和其孩子結點都是紅色的,則違反了性質4 
	* (3)刪除y將導致先前包含y的任何路徑上的黑結點樹少一個,破壞了性質5。 
	* 解決方案是:被刪除的結點黑色屬性下移到其孩子結點x上。此時性質5都得以保持,於是存在2種情況: 
	* (1)x原來為紅色,此時孩子結點屬性是紅黑,此時破壞了性質(1),(4),如果x還是樹根則,破壞了性質(2) 
	*       處理方式為:將x重新著色為黑色(此操作同時去除其多餘的黑色屬性),處理完畢,紅黑樹性質得以保持 
	* (2)x原來為黑色,此時x的屬性為雙重黑色,破壞了性質(1),若x為樹根,則可以只是簡單的消除x多餘的黑色屬性 
	*       否則需要做必要的旋轉和顏色修改 
	*/  
	public static boolean rbDelete(Node node, int data) {
		// TODO Auto-generated method stub
		Node now = rbSearch(node,data);
		Node pre = now;
		Node son = null;
		if(now == null)
			return false;
		if(now.lson != null && now.rson != null){
			now = rbSuccessor(now.rson);
			pre.data = now.data;
		}else if(now.lson != null)
			son = now.lson;
		 else if(now.rson != null)
			son = now.rson;
		
		if(son != null)
			son.parent = now.parent;
		if(now.parent == null)
			root = son;
		else{
			if(now.parent.lson == now)
				now.parent.lson = son;
			else now.parent.rson = son;
		}
		if(now.color == BLACK)
			rbTreeDeleteFixup(root,now.parent,son); 
		now.free();
		return true;
	}

	private static void rbTreeDeleteFixup(Node root,Node parent, Node node) {
		// TODO Auto-generated method stub
		Node brother = null;
		while( ( node==null || node.color==BLACK ) && node != root){
			if(node == parent.lson){
				brother = parent.rson;
				//情況1:如果兄弟結點為紅色,則parent顏色比為黑色,此時調整顏色,並左旋,使得brother和  
				//parent位置調換,此操作不破壞別的性質,並將情況1變化為情況2,3,4  
				if(brother.color == RED){
					parent.color = RED;
					brother.color = BLACK;
					LL(parent);
					brother = parent.rson;
				}
				 //情況2,這裡沒有加brother==black是因為經過情況1定然滿足
				 //brother有兩個黑色結點(NULL也為黑色結點):將x和brother抹除一重黑色  
				 //具體操作為,brother的顏色變為紅,x結點上移到其父結點
				if((brother.lson == null || brother.lson.color == BLACK) && 
					(brother.rson == null || brother.rson.color == BLACK)){
					brother.color = RED;
					node = parent;
					parent = parent.parent;
				}else{
					//從上面的if中已經知道兩個孩子不都是黑色
					//情況3: brother左孩子為紅色結點,右孩子為黑色結點  
					if(brother.rson == null || brother.rson.color == BLACK){
						brother.lson.color = BLACK;
						brother.color = RED;
						RR(brother);//右旋使情況3變化為情況4 
						brother = parent.rson;//因為旋轉,重置
					}
					//情況4:brother的右孩子為紅色結點:  
	                //交換brother和parent的顏色和位置,使得x的2重黑色屬性中的一重轉移到其parent上  
	                //此時到brother的右孩子的黑結點數少一,於是將右結點的顏色置黑,紅黑樹性質得以保持 
					brother.color = parent.color;
					parent.color = BLACK;
					brother.rson.color = BLACK;
					LL(parent);
					node = root;
				}
			}else{
				brother = parent.lson;
				//情況1:如果兄弟結點為紅色,則parent顏色比為黑色,此時調整顏色,並左旋,使得brother和  
				//parent位置調換,此操作不破壞別的性質,並將情況1變化為情況2,3,4  
				if(brother.color == RED){
					parent.color = RED;
					brother.color = BLACK;
					RR(parent);
					brother = parent.lson;
				}
				 //情況2,這裡沒有加brother==black是因為經過情況1定然滿足
				 //brother有兩個黑色結點(NULL也為黑色結點):將x和brother抹除一重黑色  
				 //具體操作為,brother的顏色變為紅,x結點上移到其父結點
				if((brother.lson == null || brother.lson.color == BLACK) && 
					(brother.rson == null || brother.rson.color == BLACK)){
					brother.color = RED;
					node = parent;
					parent = parent.parent;
				}else{
					//從上面的if中已經知道兩個孩子不都是黑色
					//情況3: brother右孩子為紅色結點,左孩子為黑色結點  
					if(brother.lson == null || brother.lson.color == BLACK){
						brother.rson.color = BLACK;
						brother.color = RED;
						LL(brother);//右旋使情況3變化為情況4 
						brother = parent.rson;//因為旋轉,重置
					}
					//情況4:brother的右孩子為紅色結點:  
	                //交換brother和parent的顏色和位置,使得x的2重黑色屬性中的一重轉移到其parent上  
	                //此時到brother的右孩子的黑結點數少一,於是將右結點的顏色置黑,紅黑樹性質得以保持 
					brother.color = parent.color;
					parent.color = BLACK;
					brother.lson.color = BLACK;
					RR(parent);
					node = root;
				}
			}
		}
		if(node != null)
			node.color = BLACK;
	}
	/**
	 * 紅黑樹的左轉與AVL的不同,LL是root.left.right提上去
	 * @param node
	 */
	public static void LL(Node node) {
		// TODO Auto-generated method stub
		Node son = node.rson;
		node.rson = son.lson;
		if(son.lson != null)
			son.lson.parent = node;
		son.parent = node.parent;
		//node 為樹根
		if(node.parent == null)
			root = son;
		else{
			if(node.parent.lson == node)
				node.parent.lson = son;
			else node.parent.rson = son;
		}
		son.lson = node;
		node.parent = son;
	}

	public static void RR(Node node) {
		// TODO Auto-generated method stub
		Node son = node.lson;
		node.lson = son.rson;
		if(son.rson != null)
			son.rson.parent = node;
		son.parent = node.parent;
		//node 為樹根
		if(node.parent == null)
			root = son;
		else{
			if(node.parent.lson == node)
				node.parent.lson = son;
			else node.parent.rson = son;
		}//要修改parent,parent的左右,本身指標
		son.rson = node;
		node.parent = son;
	}
	
}