紅黑樹java實現ADT

一、封裝ADT

    此類爲參考算法導論用java編寫。

package tree;

import java.util.concurrent.LinkedBlockingQueue;

/**
 * 紅黑樹ADT(Abstract Data Struct)
 * 本類純屬虛構,如有雷同純屬巧合。本類爲個人學習研究編寫,如有問題可指正。
 * 
 * 
 * 紅黑樹是avl樹的一種,一般avl樹使用樹高和旋轉操作來保持平衡。類似的紅黑樹使用顏色和旋轉來保持平衡。
 * 
 * 紅黑樹的插入比較簡單,在按照搜索樹插入一個節點後,將其染成紅色,由於可能與父節點形成連續紅色,所以需要簡單調整。將當前校對的節點稱爲基準節點。
 * 1. 如果其叔節點爲空,在進行一次單旋即可。
 * 1. 如果其叔節點爲紅色,則進行簡單的變色,就可以將基準節點上移。
 * 2. 如果其叔節點爲黑色則可進行一次單旋,或者雙旋,相間改變顏色即可
 * 
 * 紅黑樹的刪除比較複雜,首先按照搜索樹刪除一個節點(此過程本身已經很複雜)。然後將所有情況轉換成同一種最終情況,稍微變換顏色即可
 * 如果最終刪除的節點是個紅節點,則不會破壞性質。相反,則基準節點路徑將少一個黑色節點。需要通過旋轉操作移過來
 * 1. 如果基準節點是紅節點,則直接染黑即可。
 * 2. 如果基準節點是黑節點,則看其兄弟結點。如果兄弟結點是紅色,則進行單旋移動一個節點去基準節點路徑上並保持原來的性質
 * 3. 如果兄弟結點是黑色。則看兄弟結點的子結點。 分以下幾種情況
 * 4. 如果子結點都不存在,或者都爲黑色,或者只存在一個且爲黑色:直接將兄弟結點染成紅色,並將基準節點上移。此時基準節點子樹路徑黑節點數量一致
 * 5. 對於兄弟結點爲右子樹的情況(對稱同理)。有如下兩種情況,兩種情況需順序判斷,情況1可以轉換爲情況2
 * 情況1. 如果兄弟結點的左子節點存在且爲紅色,則進行一次單旋,變換顏色可達到情況2。
 * 情況2. 然後如果右結點存在且爲紅色,則進行一次單旋變換顏色即可完成。
 * 
 * 
 * @author Jane
 * @e-mail [email protected]
 */
public class RedBlackTtree {
	
	// 樹根
	private static Node root = null;

	
	/**
	 * 開放API
	 */
	
	
	// 插入值
	public boolean addVal(int val) {
		if(root == null) {
			root = new Node(val);
			root.color=Color.black;
			return true;
		}
		try {
			addValue(root,val);
		} catch(Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	// 刪除值
	public boolean delVal(int val) {
		try {
			if(root == null)
				throw new RuntimeException("tree root node is null");
			this.deleteValue(root, val);
		} catch (Exception e) {
			return false;
		}
		
		return true;
	}
	
	// 打印序列
	public void printTree() {
		this.printSortedArray(root);
		System.out.println();
	}
	
	// 層次打印
	public void printTreeByLayer() {
		if(root == null)
			return;
		printTreeByLayer(root);
	}
	
	public boolean validTree() {
		if(isValidRedBlackTree(root)) {
			System.out.println("正確的紅黑樹");
			return true;
		}else {
			System.out.println("錯誤的紅黑樹");
			return false;
		}
	}
	
	
	/**
	 * 內部實現
	 */
	
	
	/**
	 * 先按照二叉搜索樹插入節點,然後從插入節點開始調整使樹維護紅黑樹的性質
	 * @param node 節點
	 * @param val  值
	 */
	private void addValue(Node node, int val) {
		
		if(val<node.val && node.left!=null)
		  addValue(node.left, val);
		else if (val<node.val && node.left==null) {
			Node leaf = new Node(val);
			leaf.parent=node;
			node.left = leaf;
			leaf.color = Color.red;
			fixRedBlack(leaf);
			return;
		}
		
		if(val>=node.val && node.right!=null)
		  addValue(node.right, val);
		else if(val>=node.val && node.right == null) {
			Node leaf = new Node(val);
			leaf.parent=node;
			node.right = leaf;
			leaf.color = Color.red;
			fixRedBlack(leaf);
			return;
		}
		
	}
	
	/**
	 * 調整節點和顏色,使得樹保持紅黑樹性質
	 * @param leaf 葉子節點
	 */
	private void fixRedBlack(Node leaf) {
		// 終止條件:找到根節點,或者父節點是黑色
		while(leaf.parent !=null && leaf.parent.color==Color.red) {
			Node p = leaf.parent;
			Node pp = p.parent;
			
			// 父節點爲祖節點左子樹
			if(p == pp.left) {
				Node sr = pp.right;  // 獲得叔節點
				if(sr == null) {     // 叔節點爲空,進行一次右旋即可
					Node ppp = pp.parent;
					if(leaf == p.left) {
						turnRight(p);
						p.color = Color.black;
						pp.color = Color.red;
						
						if(pp == ppp.left)
							ppp.left = p;
						else
							ppp.right = p;
					}
					else {
						turnLeftThenRight(p);
						leaf.color = Color.black;
						pp.color = Color.red;
						
						if(pp == ppp.left)
							ppp.left = leaf;
						else
							ppp.right = leaf;
					}
				} else if(sr.color ==Color.red) { // 叔節點爲紅色,變化顏色繼續循環
					pp.color = Color.red;
					sr.color = Color.black;
					p.color = Color.black;
				} else {					// 叔節點爲黑色,做最終調整
					Node ppp=pp.parent;	
					if(leaf == p.left) {	// 直線則單旋
						if(ppp == null) {
							turnRight(p);
							break;
						}
						else if(pp == ppp.left)
							ppp.left = turnRight(p);
						else
							ppp.right = turnRight(p);
						p.color=Color.black;
						pp.color=Color.red;
					} else {				// 非直線則雙旋
						if(ppp == null) {
							turnLeftThenRight(p);
							break;
						}
						if( pp ==ppp.left)
							ppp.left = turnLeftThenRight(p);
						else 
							ppp.right = turnLeftThenRight(p);
						leaf.color = Color.black;
						pp.color = Color.red;
					}
					break;
				}
				// 葉子節點上移
				leaf = pp; 
			} else {		// 對稱同理:父節點爲祖節點右子樹
				Node sl =pp.left;
				if(sl == null) {
					Node ppp = pp.parent;
					if(leaf == p.right) {
						turnLeft(p);
						pp.color = Color.red;
						p.color = Color.black;
						if(pp == ppp.left)
							ppp.left = p;
						else
							ppp.right = p;
					} else {
						turnRightThenLeft(p);
						leaf.color = Color.black;
						pp.color = Color.red;
						if(pp == ppp.left)
							ppp.left = leaf;
						else
							ppp.right = leaf;
					}
				} else if (sl.color == Color.red) {
					sl.color = Color.black;
					p.color = Color.black;
					pp.color = Color.red;
				} else {
					Node ppp = pp.parent;
					if(leaf == p.left) {
						if(ppp == null) {
							turnRightThenLeft(p);
						} else if(pp == ppp.left) {
							ppp.left = turnRightThenLeft(p);
						} else {
							ppp.right = turnRightThenLeft(p);;
						}
						
						leaf.color = Color.black;
						pp.color = Color.red;
					} else {
						if(ppp == null) {
							turnRight(p);
						} else if (pp ==ppp.left) {
							ppp.left = turnRight(p);
						} else {
							ppp.right = turnRight(p);
						}
						p.color = Color.black;
						pp.color = Color.red;
					}
					break;
				}
				leaf = pp;
			}
		}
		
		// 保持根節點爲黑色
		if(leaf.parent == null)
			leaf.color=Color.black;
	}
	
	
	/**
	 * 先按照二叉搜索書刪除節點,然後從相關節點開始調整使樹保持紅黑樹性質
	 * @param val
	 */
	private void deleteValue(Node t, int val) {
		if(t == null) {
			return;
		}
		
		// 刪除該節點
		if(t.val == val) {
			Node p = t.parent;
			Node leaf;
			Color originColor = t.color;  // 記錄被刪除節點的顏色
			if(t.left == null && t.right == null) {
				// 葉子節點直接刪除
				transplant(t, null);  
				return;
			}
			if(t.left == null && t.right !=null) {
				leaf = t.right;
				transplant(t, t.right);
			} else if(t.left !=null && t.right == null) {
				leaf = t.left;
				transplant(t, t.left);
			} else {
				// 找該節點右子樹一個最小結點替換要刪除的節點
				Node sub = findMinNode(t.right);
				originColor = sub.color;
				leaf = sub.right;
				sub.parent.left = leaf;
				if(leaf !=null)
					leaf.parent = sub.parent;
				transplant(t, sub);
				sub.left = t.left;
				sub.color = t.color; 
				sub.right = t.right;
				t.right.parent = sub;
				sub.left.parent = sub;
				t = null; // help gc
			}
			
			// 如果leaf結點爲null則往後不再有路徑,自然也無需調整
			// 如果被刪除的是紅節點則性質必然繼續保持(紅結點的父節點必爲黑色,刪除紅節點不影響路徑黑節點數量)
			if(leaf != null && originColor != Color.red)
				fixRedBlackWhenDel(leaf);
			
			return;
		}
		
		if(val<t.val)
			deleteValue(t.left, val);
		else
			deleteValue(t.right, val);
	}
	
	/**
	 * 移植替換要刪除的節點
	 * @param u 待刪除的節點
	 * @param v 被移植的節點
	 */
	private void transplant(Node u, Node v) {
		if(u.parent == null) {
			root = v;
		} else if(u == u.parent.left) {
			u.parent.left=v;
		} else
			u.parent.right = v;
		
		if(v != null) {
			v.parent = u.parent;
		}
	}
	
	private void fixRedBlackWhenDel(Node leaf) {
		// 如果leaf是紅節點,只需將其顏色變黑即可保持性質
		// 如果是黑節點,則包含leaf的路徑都將少一個黑節點,需要通過旋轉移一個黑節點過來,並保持其他路徑黑節點數量不變
		while(leaf.color == Color.black && leaf.parent !=null) {
			Node p = leaf.parent;
			if(leaf == p.left) {
				Node sr = p.right;
				if(sr == null) {// 單路徑直接往上
					leaf = p;
				}else if(sr.color == Color.red) {
					Node pp = p.parent;
					if(pp == null) {  // 當前父節點是跟節點 
						turnLeft(p.right);
					}else if(p == pp.left) {
						pp.left = turnLeft(p.right);
					} else {
						pp.right = turnLeft(p.right);
					}
					p.color = Color.red;
					sr.color = Color.black;
				} else {
					Node wl = sr.left;
					Node wr = sr.right;
					if((wl==null && wr == null)||(wl==null && wr.color==Color.black)||(wr==null && wl.color == Color.black)||(wl.color==Color.black && wr.color == Color.black)) {
						sr.color = Color.red;
						leaf = p;  // 包含p節點的路徑 黑節點數量都少1個,所以將leaf上移
					} else if(wr !=null && wr.color == Color.red) {
						Node pp = p.parent;
						if(pp == null) {
							turnLeft(sr);
						}else if( p == pp.left) {
							pp.left=turnLeft(sr);
						}else {
							pp.right=turnLeft(sr);
						}
						p.color = Color.black;
						sr.color = Color.red;
						wr.color = Color.black;
						break;
					} else if(wl !=null && wl.color == Color.red) {
						p.right=turnRight(sr.left);
						p.right.color = Color.black;
						sr.color = Color.red;
					}
				}
			} else {
				Node sl = p.left;
				Node pp = p.parent;
				if (sl == null)
					leaf = p;
				else if(sl.color == Color.red) {
					if(pp == null) {
						turnRight(p.left);
					}else if(p==pp.left) {
						pp.left = turnRight(p.left);
					} else
						pp.right = turnLeft(p.left);
						sl.color =Color.black;
						p.color = Color.red;
				} else {
					Node wl = sl.left;
					Node wr = sl.right;
					if((wl==null && wr == null)||(wl==null && wr.color==Color.black)||(wr==null && wl.color == Color.black)||(wl.color==Color.black && wr.color == Color.black)) {
						sl.color = Color.red;
						leaf = p;
					} else if(wl != null && wl.color == Color.red) {
						if(pp==null) {
							turnRight(p.left);
						}else if(p == pp.left)
							pp.left = turnRight(sl);
						else
							pp.right = turnRight(sl);
						
						p.color = Color.black;
						sl.color = Color.red;
						wl.color = Color.black;
						break;
					} else if(wr != null && wr.color == Color.red) {
						p.left=turnLeft(sl.right);
						p.left.color= Color.black;
						sl.color = Color.red;
					}
				}
			}
		}
		
		leaf.color = Color.black;
	}
	
	private Node findMinNode(Node node) {
		if(node.left == null)
			return node;
		return findMinNode(node.left);
	}
	
	
	// 右旋
	private Node turnRight(Node t) {
		Node p = t.parent;
		p.left = t.right;
		t.right = p;
		p.parent =t;
		return t;
	}
	
	// 左旋
	private Node turnLeft(Node t) {
		Node p = t.parent;
		p.right = t.left;
		t.left = p;
		p.parent = t;
		return t;
	}
	
	// 先左旋再右旋
	private Node turnLeftThenRight(Node t) {
		Node p = t.parent;
		Node s = turnLeft(t.right);
		p.left = s;
		s.parent = p;
		return turnRight(p.left);
	}
	
	// 先右旋轉再左旋
	private Node turnRightThenLeft(Node t) {
		Node p = t.parent;
		Node s = turnRight(t.left);
		p.right = s;
		s.parent = p;
		return turnLeft(p.right);
	}
	

	/**
	 * 輸出樹的值
	 * 
	 */
	
	// 中序遍歷  按從小到大的順序打印
	private void printSortedArray(Node node) {
		if(node ==null)
			return;
		printSortedArray(node.left);
		System.out.print(node.val + " ");
		printSortedArray(node.right);
		
	}
	
	/**
	 * 按層次打印樹並輸出顏色
	 * @param node
	 */
	private void printTreeByLayer(Node node) {

		int count = 0;
		int layerLen = 0;
		int nextLayerLen = 0;
		LinkedBlockingQueue<Node> queue = new LinkedBlockingQueue<>();
		queue.offer(node);
		layerLen++;
		while(!queue.isEmpty()) {
			Node t = queue.remove();
			count++;
			
			if(t.left != null) {
				queue.offer(t.left);
				nextLayerLen++;
			}
			if(t.right != null) {
				queue.offer(t.right);
				nextLayerLen++;
			}
			
			System.out.print(t.val + "-" + t.color.name());
			if(layerLen == count) {
				System.out.println();
				layerLen = nextLayerLen;
				nextLayerLen = 0;
				count=0;
			} else {
				System.out.print(" ");
			}
			
			
		}
	}
	
	
	/**
	 * 校驗紅黑樹是否正確
	 */
	
	private boolean isValidRedBlackTree(Node root) {
		if(root == null)
			return true;
		
		// 根節點校驗
		if(root.color != Color.black) {
			return false;
		}
		
		// 連續紅節點校驗
		if(!validColor(root, false))
			return false;
		
		// 路徑黑節點數量校驗
		if(!validBlackCount(root))
			return false;
		// 值順序校驗
		if(!validSortValue(root))
			return false;
		
		// 滿足所有必要條件,則充分證明是棵發育良好的紅黑樹
		return true;
	}
	
	private boolean validBlackCount(Node root) {
		
		if(countBlackNodes(root)<0)
			return false;
		return true;
	}
	
	private int countBlackNodes(Node root) {
		int cur = 0;
		
		if(root.color == Color.black)
			cur++;
		
		int countLeft=0;
		int countRight=0;
		
		if(root.left == null && root.right == null)
			return cur; 
		if(root.left != null)
		 countLeft = countBlackNodes(root.left);
		
		if(root.right != null)
		 countRight = countBlackNodes(root.right);
		
		if(root.left == null && root.right !=null)
			return countRight + cur;
		if(root.left != null && root.right == null)
			return countLeft + cur;
		
		if(countLeft == -1 || countRight == -1 || countLeft != countRight)
			return -1;
		
		return countLeft + cur;
	}
	
	private boolean validSortValue(Node root) {
		boolean left = true;
		boolean right = true;
		if(root.left!=null&& root.left.val<root.val) {
			left = validSortValue(root.left);
		} else if( root.left!=null && root.left.val >= root.val)
			return false;
		
		if(root.right!=null && root.right.val >= root.val)
		    right = validSortValue(root.right);
		else if(root.right!=null && root.right.val < root.val)
			return false;
		
		return left && right;
	}
	
	private boolean validColor(Node root, boolean parentRed) {
		if(root.color == Color.red && parentRed) // 路徑連續紅節點
			return false;
		else if(root.color == Color.red)
			parentRed = true;
		else if(root.color == Color.black)
			parentRed = false;
		
		boolean left=true;
		boolean right=true;
		if(root.left!=null)
			left=validColor(root.left, parentRed);
		if(root.right!=null)
			right=validColor(root.right, parentRed);
		
		return left && right;
	}
	
	/**
	 * 節點設計
	 * 包含一個帶有父節點的引用
	 * @author 75279
	 *
	 */
	class Node{
		Node left;
		Node right;
		Color color;
		Node parent;
		int val;
		
		Node(int val){
			this.val=val;
		}

		public Node getLeft() {
			return left;
		}

		public void setLeft(Node left) {
			this.left = left;
		}

		public Node getRight() {
			return right;
		}

		public void setRight(Node right) {
			this.right = right;
		}

		public Color getColor() {
			return color;
		}

		public void setColor(Color color) {
			this.color = color;
		}

		public Node getParent() {
			return parent;
		}

		public void setParent(Node parent) {
			this.parent = parent;
		}

		public int getVal() {
			return val;
		}

		public void setVal(int val) {
			this.val = val;
		}

	}
	
	// 顏色枚舉
	enum Color{
		red(1),
		black(0);
		Color(int val){
			value =val;
		}
		int value;
		public int getColor(){
			return value;
		}
	}
}

二、測試主類:

import tree.RedBlackTtree;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		RedBlackTtree redBlackTtree = new RedBlackTtree();
		redBlackTtree.addVal(13);
		redBlackTtree.addVal(51);
		redBlackTtree.addVal(1);
		redBlackTtree.validTree();
		redBlackTtree.addVal(33);
		redBlackTtree.addVal(150);
		redBlackTtree.addVal(6);
		redBlackTtree.validTree();
		redBlackTtree.addVal(12);
		redBlackTtree.addVal(15);
		redBlackTtree.addVal(11);
		redBlackTtree.validTree();
		redBlackTtree.addVal(25);
		redBlackTtree.validTree();
		
		redBlackTtree.printTree();
		redBlackTtree.printTreeByLayer();
		
		// 刪除一個節點 並校驗 並打印
		redBlackTtree.delVal(150);
		redBlackTtree.validTree();
		
		redBlackTtree.delVal(12);
		redBlackTtree.validTree();
		
		redBlackTtree.delVal(33);
		redBlackTtree.validTree();
		
		redBlackTtree.delVal(1);
		redBlackTtree.validTree();
		
		redBlackTtree.delVal(51);
		redBlackTtree.validTree();
		
		redBlackTtree.printTree();
		redBlackTtree.printTreeByLayer();
		
	}

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章