標籤:紅/黑樹狀結構 平衡樹 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 stubthis();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 stubif(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 stubNode 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 stubNode 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){//修複情況1gparent.color = RED;p.color = BLACK;uncle.color = BLACK;node = gparent;}else{ //叔父不存在或者存在但是顏色是黑色的,則必須通過尋轉來配合改變顏色來保持性質2if(node == p.rson){//修複情況2node = 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{ //叔父不存在或者存在但是顏色是黑色的,則必須通過尋轉來配合改變顏色來保持性質2if(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 stubNode 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 stubNode 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 stubNode 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 stubNode 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;}}
RBT紅/黑樹狀結構-JAVA版本