RBT紅黑樹-JAVA版本
阿新 • • 發佈:2019-01-24
這個玩意程式碼量巨大,模仿著別人寫了整整一天...
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; } }