紅黑樹是平衡樹的一種,保證最壞狀況下操做時間複雜度爲O(lgo(n))。紅黑樹的應用比較普遍,好比做爲C++中STL的set和map的底層數據結構,Java集合中TreeSet和TreeMap的底層數據結構等。學習紅黑樹,能夠把二叉查找樹做爲參考,這樣有助於加深理解。紅黑樹的操做主要包括節點旋轉、插入、刪除等操做,下面我們就一一來看:java
紅黑樹總體節點圖示以下:node
旋轉是保持二叉搜索樹(紅黑樹)局部性質的操做,包括左旋和右旋。當在節點x上作左旋時,假設它的右孩子爲y而不是T.nil,X可爲其右孩子不是T.nil的任何節點,同理,X作右旋也是同樣的。git
僞代碼以下(左旋)github
左旋Java示例代碼:算法
/** * 左旋操做 */ private void leftRotate(Node x) { Node y = x.right; x.right = y.left; if (y.left != nil) { y.left.parent = x; } y.parent = x.parent; if (x.parent == nil) { root = y; } else if (x == x.parent.left) { x.parent.left = y; } else { x.parent.right = y; } x.parent = y; y.left = x; }
在O(log(n))時間內插入一個節點,RB-INSERT過程完成該操做,像一個普通的二叉搜索樹插入操做同樣,而後將要插入的節點Z着爲紅色。爲了保持紅黑樹的性質,調用一個輔助函數RB-INSERT-FIXUP來對節點從新着色並旋轉。待插入節點Z已保存了數據項。數據結構
RB-INSERT-FIXUP過程僞代碼app
注意:Case1屬於if判斷條件,Case2和Case3屬於else語句,Case2是else語句中的if判斷語句的。具體能夠看代碼。ide
插入過程Java代碼示例:函數
/** * 往紅黑樹中插入一個元素 * @param data */ public void insert(int data) { Node y = nil; Node x = root; if (root == nil) { root = newNode(data); root.color = Node.BLACK; } else { while (x != nil) { if (data < x.data) { y = x; x = x.left; } else if (data > x.data) { y = x; x = x.right; } else { return; } } Node z = newNode(data); z.parent = y; if (data < y.data) { y.left = z; } else { y.right = z; } if (y.color == Node.RED) { insertFixup(z); } } }
RB-INSERT-FIXUP過程分析學習
第1-15行的while循環在每次迭代的開始始終保持如下3個條件是不變的
循環終止條件:
循環終止由於z.p是黑色的(若是z是根節點,則z.p是黑色哨兵節點),這樣,在循環終止時並無違反性質4,惟一可能不成立的就是性質2,不過第16行(僞代碼中的行)恢復了這個性質。
循環保持條件:
循環保持有6中條件,其中3種和另外3種是對稱的,這取決於z的父節點z.p是z的祖父節點z.p.p的左孩子仍是右孩子。狀況一、二、3的區別就在於z 的父節點的兄弟節點(z的叔節點)的顏色不一樣,加入y指向z的叔節點,若是y的顏色是紅色的,則執行狀況1,不然轉向狀況2和3。在全部的3種狀況中,z 的祖父節點z.p.p是黑色的,由於他的父節點z.p是紅色的,故性質4只有在z和z.p之間被破壞了。
狀況1:z的叔節點y是紅色的
此時對應Case1,將z.p和y都着成黑色,解決z和z.p都是紅色的問題,將z.p.p着成紅色保持性質5,而後把z.p.p做爲新節點z來繼續進行while循環,指針z上移兩層
狀況2:z的叔節點y是黑色的且z是一個右孩子
狀況3:z的叔節點y是黑色的且z是一個左孩子
兩種狀況中,y都是黑色的,經過z是左孩子仍是右孩子來區分,狀況2中能夠經過一個左旋來轉化爲狀況3,此時z爲左孩子。由於z和z.p都是紅節點,對黑 高(從某個節點出發,不含該節點,到達一個葉節點的任意一條簡答路徑上的黑色節點數爲該節點的黑高)和性質5都無影響。在狀況3中,改變某些節點顏色並作 一次右旋,保持性質5,這樣不在有兩個紅色節點相鄰,處理完畢,此時z.p是黑色的,因此無需再執行while循環了。
RB-INSERT-FIXUP的Java代碼實現
/** * 插入節點後不知足紅黑樹條件時來修復 * @param z 待插入的節點 */ private void insertFixup(Node z) { // y爲z節點的叔叔節點 Node y = null; while (z.parent.color == Node.RED) { if (z.parent == z.parent.parent.left) { y = z.parent.parent.right; if (y.color == Node.RED) { z.parent.color = Node.BLACK; y.color = Node.BLACK; z.parent.parent.color = Node.RED; z = z.parent.parent; } else { if (z == z.parent.right) { z = z.parent; leftRotate(z); } z.parent.color = Node.BLACK; z.parent.parent.color = Node.RED; rightRotate(z.parent.parent); } } else { y = z.parent.parent.left; if (y.color == Node.RED) { z.parent.color = Node.BLACK; y.color = Node.BLACK; z.parent.parent.color = Node.RED; z = z.parent.parent; } else { if (z == z.parent.left) { z = z.parent; rightRotate(z); } z.parent.color = Node.BLACK; z.parent.parent.color = Node.RED; leftRotate(z.parent.parent); } } } root.color = Node.BLACK; }
刪除操做與插入操做相比,略顯複雜,與插入操做同樣,也要花費O(log(n))時間。從紅黑樹中刪除節點,需設計一個供TREE-DELETE調用的子過程TRANSPLANT,並應用到紅黑樹中,TRANSPLANT過程用來調整兩個節點的關係,其中的一個節點要替換掉另外一個節點
TRANSPLANT過程
u節點表示將要被替換掉的節點,v節點是準備替換u節點的
RB-TRANSPLANT的Java代碼實現
/** * 兩個節點的替換,newNode替換oldNode * @param oldNode * @param newNode */ private void transplant(Node oldNode, Node newNode) { if (oldNode.parent == nil) { root = newNode; } else if (oldNode == oldNode.parent.left) { oldNode.parent.left = newNode; } else { oldNode.parent.right = newNode; } newNode.parent = oldNode.parent; }
RB-DELETE過程
RB-DELETE中,z節點是要被刪除的節點,其中記錄了節點y的蹤影,y有可能致使紅黑樹性質破壞,當想刪除節點z,且z的子節點少於2個時,z從書中刪除,並讓y稱爲z。當z有兩個子節點時,y應該是z的後繼,而且將y移到z的位置。在節點被刪除或者移動時,必須記住y的顏色,而且記錄節點x的蹤影,將x移到y的原來的位置,由於節點x可能引發紅黑樹性質破壞。刪除節點z後,RB-DELETE-FIXUP過程經過改變顏色和執行旋轉來恢復紅黑樹性質。
咱們保存節點x的蹤影,使它移至節點y的原來位置。第四、7和11行的賦值語句令x或指向y的惟一子節點或指向y哨兵T.nil(y沒有子節點的話)。 第五、8或14行調用RB-TRANSPLANT時,傳遞的第2個參數與x相同
若是y是黑色的,則有可能引入一個或多個破壞紅黑色的狀況,因此在第22行調用RB-TRANSPLANT來恢復紅黑樹性質。若是y是紅色的,當y被刪除或移動時,紅黑樹性質依然成立,由於(1) 樹中黑高沒有變化 (2) 不存在兩個相鄰的紅節點,由於y在樹中佔據了z的位置,z的位置確定不會違反紅黑樹性質的。另外,若是y是z的右孩子,則y的原右孩子x代替y,若是y是紅色的,則x必定是黑色的,一次用x替代y不會使兩個紅節點相鄰。 (3) 若是y是紅色的,就不會是根節點,因此根節點仍然是黑色的。
y爲黑節點狀況分析
RB-DELETE的Java代碼實現
/** * 從紅黑樹中移除一個元素 * @param data 待移除的元素 */ public void remove(int data) { Node z = contains(data); if (z == null) { return; } Node x; Node y = z; int yOldColor = y.color; if (z.left == nil) { x = z.right; transplant(z, x); } else if (z.right == nil) { x = z.left; transplant(z, x); } else { y = z.right; while (y.left != nil) { y = y.left; } yOldColor = y.color; x = y.right; if (y.parent == z) { x.parent = y; } else { transplant(y, y.right); y.right = z.right; y.right.right = y; } transplant(z, y); y.left = z.left; y.left.parent = y; y.color = z.color; } if (yOldColor == Node.BLACK) { removeFixup(x); } }
RB-DELETE-FIXUP過程
ps:圖片上的狀況層次關係對應不是很齊,能夠看碼來的清楚
4種狀況的轉換關係
不管哪一個Case,要麼是爲了到達平衡兩個節點,路徑上黑色數目相同的目的,要麼是經過變形間接達到這個目的。
while循環的目標是將額外的黑色沿樹上移,直到:
代碼中的4種狀況圖示
上圖給出了代碼中出現的四種狀況,再具體研究每個狀況時,先看看如何證明每種狀況中的變換保證性質5。關鍵思想是在每種狀況下,從子樹的跟(包括跟)到每棵子樹之間的黑色節點個數(包括x的額外黑色)並不被變換改變,所以,若是性質5在變換以前成立,則在變換以後也成立。好比:在狀況1中,在變換先後,根節點到子樹a或b之間的黑色節點數是3(由於x增長了一層黑色),相似的,在變換先後根節點到其他的4個子樹的葉節點中的任何一個的黑節點數是2。在圖13-7(b)中,計數是還要包括所示子樹的根節點的color屬性的值,它是RED或是BLACK,若是定義count(RED)=0以及count(BLACK)=1,那麼變換先後根節點至a的黑節點都爲2+count(c)。在此狀況下,變換以後新節點x具備color屬性的c,可是這個節點的顏色是紅黑(若是c=RED)或者雙重黑色的(若是c=BLACK)。其餘狀況能夠相似加以驗證。
狀況1:x的兄弟節點w是紅色的
狀況1(見RB-DELETE-FIXUP的第5-8行和圖13-7(a))發生在節點x的兄弟節點w爲紅色時。由於w必須有黑色子節點,全部能夠改變w的x.p的顏色,而後對x.p作一次左旋而不違反紅黑樹的任何性質。如今x的新兄弟節點是旋轉以前w的某個子節點,其顏色爲黑色。這樣,就將狀況1轉變爲了狀況二、3或者4。
當節點w爲黑色時,屬於狀況二、3或者4;這些狀況有w的子節點顏色來區分了。
狀況2:x的兄弟節點w是黑色的,並且w的兩個子節點都是黑色的
在狀況2(見RB-DELETE-FIXUP的第10-11行和圖13-7(b)),w的兩個子節點都是黑色的,由於w也是黑色的,因此從x和w上去掉一重黑色,使得x只有一重黑色而w爲紅色。爲了補償從x和w中去掉的一重黑色,在原來是紅色或黑色的x.p上增長一重額外的黑色。經過將x.p做爲新節點x來重複while循環。注意到,若是經過狀況1進入到狀況2,則新節點x就是紅黑色的,由於原來的x.p是紅色的,所以,新節點x的color屬性值爲RED,而且在測試循環條件後循環終止。而後,在第23行處將新節點x着爲(單一)黑色。
狀況3:x的兄弟節點w是黑色的,w的左孩子是紅色的,w的右孩子是黑色的
狀況3(見RB-DELETE-FIXUP的第13-16行和圖13-7(c))發生在w爲黑色且其左孩子爲紅色,右孩子爲黑色時。能夠交換w和其左孩子w.left的顏色,而後對w進行右旋而不違反紅黑樹的性質。如今x的新節點w是一個有紅色右孩子的黑色節點,這樣咱們就從狀況3轉換成了狀況4。
狀況4:x的兄弟節點w是黑色的,且w的右孩子是紅色的
狀況4(見RB-DELETE-FIXUP的第17-21行和圖13-7(d))發生在節點x的兄弟節點w爲黑色且w的右孩子爲紅色時,經過進行某些顏色修改並對x.p作一次左旋,能夠去掉x的額外黑色,從而使它變爲單重黑色,並且不破壞紅黑樹性質。將x設爲設爲根root後,當while循環測試其循環條件時,循環終止。
RB-DELETE-FIXUP的Java代碼實現
/** * 移除一個元素後紅黑樹不符合要求時的修復方法 * @param x */ private void removeFixup(Node x) { Node y; while (x != root && x.color == Node.BLACK) { if (x == x.parent.left) { y = x.parent.right; if (y.color == Node.RED) { //狀況1:x的兄弟節點w是紅色 y.color = Node.BLACK; x.parent.color = Node.RED; leftRotate(x.parent); y = x.parent.right; } if (y.left.color == Node.BLACK && y.right.color == Node.BLACK) { //狀況2:x的兄弟節點w是黑色,並且w的兩個子節點都是黑色的 y.color = Node.RED; x = x.parent; } else { if (y.right.color == Node.BLACK) { //狀況3:x的兄弟節點w是黑色的,w的左孩子是紅色的,w的右孩子是黑色的 y.left.color = Node.BLACK; y.color = Node.RED; rightRotate(y); y = x.parent.right; } y.color = x.parent.color; //狀況4:x的兄弟節點w是黑色的,且w的右孩子是紅色的 x.parent.color = Node.BLACK; y.right.color = Node.BLACK; leftRotate(x.parent); x = root; } } else { y = x.parent.left; if (y.color == Node.RED) { y.color = Node.BLACK; x.parent.color = Node.RED; rightRotate(x.parent); y = x.parent.left; } if (y.left.color == Node.BLACK && y.right.color == Node.BLACK) { y.color = Node.RED; x = x.parent; } else { if (y.left.color == Node.BLACK) { y.right.color = Node.BLACK; y.color = Node.RED; leftRotate(y); y = x.parent.left; } y.color = x.parent.color; x.parent.color = Node.BLACK; y.left.color = Node.BLACK; rightRotate(x.parent); x = root; } } } x.color = Node.BLACK; }
刪除分析:
紅黑樹Rbtre.java文件:
1 package tree; 2 3 /** 4 * 紅黑樹節點類 5 */ 6 class Node { 7 // 紅黑節點顏色 8 public final static int RED = 0; 9 public final static int BLACK = 1; 10 11 // ------------------------------ Instance Variables 12 13 public Node left; // 左節點 14 public Node right; // 右節點 15 public Node parent; // 父節點 16 public int color; // 節點顏色 17 public int data; // 數據域,int型數據 18 } 19 20 /** 21 * 紅黑樹類 22 * 23 */ 24 public class Rbtree { 25 26 // ------------------------------ Instance Variables 27 28 // 紅黑樹空節點 29 public Node nil; 30 31 // 紅黑樹根節點 32 public Node root; 33 34 // ------------------------------ Constructors 35 36 /** 37 * Init nil node and root node. 38 */ 39 public Rbtree() { 40 nil = new Node(); 41 nil.left = nil.right = nil.parent = nil; 42 nil.color = Node.BLACK; 43 root = nil; 44 } 45 46 // ------------------------------ Public methods 47 48 /** 49 * 往紅黑樹中插入一個元素 50 * @param data 51 */ 52 public void insert(int data) { 53 Node y = nil; 54 Node x = root; 55 56 if (root == nil) { 57 root = newNode(data); 58 root.color = Node.BLACK; 59 } else { 60 while (x != nil) { 61 if (data < x.data) { 62 y = x; 63 x = x.left; 64 } else if (data > x.data) { 65 y = x; 66 x = x.right; 67 } else { 68 return; 69 } 70 } 71 72 Node z = newNode(data); 73 z.parent = y; 74 if (data < y.data) { 75 y.left = z; 76 } else { 77 y.right = z; 78 } 79 if (y.color == Node.RED) { 80 insertFixup(z); 81 } 82 } 83 } 84 85 /** 86 * 從紅黑樹中移除一個元素 87 * @param data 待移除的元素 88 */ 89 public void remove(int data) { 90 Node z = contains(data); 91 if (z == null) { 92 return; 93 } 94 95 Node x; 96 Node y = z; 97 int yOldColor = y.color; 98 if (z.left == nil) { 99 x = z.right; 100 transplant(z, x); 101 } else if (z.right == nil) { 102 x = z.left; 103 transplant(z, x); 104 } else { 105 y = z.right; 106 while (y.left != nil) { 107 y = y.left; 108 } 109 yOldColor = y.color; 110 x = y.right; 111 112 if (y.parent == z) { 113 x.parent = y; 114 } else { 115 transplant(y, y.right); 116 y.right = z.right; 117 y.right.right = y; 118 } 119 120 transplant(z, y); 121 y.left = z.left; 122 y.left.parent = y; 123 y.color = z.color; 124 } 125 126 if (yOldColor == Node.BLACK) { 127 removeFixup(x); 128 } 129 } 130 131 /** 132 * 紅黑樹中是否包含data 133 * @param data 134 * @return 135 */ 136 public Node contains(int data) { 137 Node node = root; 138 139 while (node != null) { 140 if (data < node.data) { 141 node = node.left; 142 } else if (data > node.data) { 143 node = node.right; 144 } else { 145 return node; 146 } 147 } 148 return null; 149 } 150 151 @Override 152 public String toString() { 153 StringBuilder buf = new StringBuilder(); 154 155 if (root != nil) { 156 toStringInternal(root, buf); 157 } 158 return buf.toString(); 159 } 160 161 // ------------------------------ Private methods 162 163 /** 164 * New a initialize node, default node's color is RED 165 */ 166 private Node newNode(int data) { 167 Node node = new Node(); 168 169 node.data = data; 170 node.color = Node.RED; 171 node.left = node.right = node.parent = nil; 172 return node; 173 } 174 175 private void toStringInternal(Node node, StringBuilder buf) { 176 if (node != nil) { 177 toStringInternal(node.left, buf); 178 buf.append(node.data + (node.color == Node.RED ? "-red " : "-black ")); 179 toStringInternal(node.right, buf); 180 } 181 } 182 183 /** 184 * 兩個節點的替換,newNode替換oldNode 185 * @param oldNode 186 * @param newNode 187 */ 188 private void transplant(Node oldNode, Node newNode) { 189 if (oldNode.parent == nil) { 190 root = newNode; 191 } else if (oldNode == oldNode.parent.left) { 192 oldNode.parent.left = newNode; 193 } else { 194 oldNode.parent.right = newNode; 195 } 196 197 newNode.parent = oldNode.parent; 198 } 199 200 /** 201 * 左旋操做 202 */ 203 private void leftRotate(Node x) { 204 Node y = x.right; 205 206 x.right = y.left; 207 if (y.left != nil) { 208 y.left.parent = x; 209 } 210 y.parent = x.parent; 211 if (x.parent == nil) { 212 root = y; 213 } else if (x == x.parent.left) { 214 x.parent.left = y; 215 } else { 216 x.parent.right = y; 217 } 218 219 x.parent = y; 220 y.left = x; 221 } 222 223 /** 224 * 右旋操做 225 */ 226 private void rightRotate(Node x) { 227 Node y = x.left; 228 229 x.left = y.right; 230 if (y.right != nil) { 231 y.right.parent = x; 232 } 233 y.parent = x.parent; 234 if (x.parent == nil) { 235 root = y; 236 } else if (x == x.parent.left) { 237 x.parent.left = y; 238 } else { 239 x.parent.right = y; 240 } 241 242 x.parent = y.parent; 243 y.right = x; 244 } 245 246 /** 247 * 插入節點後不知足紅黑樹條件時來修復 248 * @param z 待插入的節點 249 */ 250 private void insertFixup(Node z) { 251 // y爲z節點的叔叔節點 252 Node y = null; 253 254 while (z.parent.color == Node.RED) { 255 if (z.parent == z.parent.parent.left) { 256 y = z.parent.parent.right; 257 if (y.color == Node.RED) { // Case1: x uncle node y is red 258 z.parent.color = Node.BLACK; 259 y.color = Node.BLACK; 260 z.parent.parent.color = Node.RED; 261 } else { 262 if (z == z.parent.right) { // Case2: x uncle node y is black, but x is right node 263 z = z.parent; 264 leftRotate(z); 265 } 266 267 z.parent.color = Node.BLACK; // Case3: x uncle node y is black, but x is left node 268 z.parent.parent.color = Node.RED; 269 rightRotate(z.parent.parent); 270 } 271 } else { 272 y = z.parent.parent.left; 273 if (y.color == Node.RED) { 274 z.parent.color = Node.BLACK; 275 y.color = Node.BLACK; 276 z.parent.parent.color = Node.RED; 277 } else { 278 if (z == z.parent.left) { 279 z = z.parent; 280 rightRotate(z); 281 } 282 283 z.parent.color = Node.BLACK; 284 z.parent.parent.color = Node.RED; 285 leftRotate(z.parent.parent); 286 } 287 } 288 } 289 290 root.color = Node.BLACK; 291 } 292 293 /** 294 * 移除一個元素後紅黑樹不符合要求時的修復方法 295 * @param x 296 */ 297 private void removeFixup(Node x) { 298 Node y; 299 300 while (x != root && x.color == Node.BLACK) { 301 if (x == x.parent.left) { 302 y = x.parent.right; 303 if (y.color == Node.RED) { //狀況1:x的兄弟節點w是紅色 304 y.color = Node.BLACK; 305 x.parent.color = Node.RED; 306 leftRotate(x.parent); 307 y = x.parent.right; 308 } 309 310 if (y.left.color == Node.BLACK && y.right.color == Node.BLACK) { //狀況2:x的兄弟節點w是黑色,並且w的兩個子節點都是黑色的 311 y.color = Node.RED; 312 x = x.parent; 313 } else { 314 if (y.right.color == Node.BLACK) { //狀況3:x的兄弟節點w是黑色的,w的左孩子是紅色的,w的右孩子是黑色的 315 y.left.color = Node.BLACK; 316 y.color = Node.RED; 317 rightRotate(y); 318 y = x.parent.right; 319 } 320 321 y.color = x.parent.color; //狀況4:x的兄弟節點w是黑色的,且w的右孩子是紅色的 322 x.parent.color = Node.BLACK; 323 y.right.color = Node.BLACK; 324 leftRotate(x.parent); 325 x = root; 326 } 327 } else { 328 y = x.parent.left; 329 if (y.color == Node.RED) { 330 y.color = Node.BLACK; 331 x.parent.color = Node.RED; 332 rightRotate(x.parent); 333 y = x.parent.left; 334 } 335 336 if (y.left.color == Node.BLACK && y.right.color == Node.BLACK) { 337 y.color = Node.RED; 338 x = x.parent; 339 } else { 340 if (y.left.color == Node.BLACK) { 341 y.right.color = Node.BLACK; 342 y.color = Node.RED; 343 leftRotate(y); 344 y = x.parent.left; 345 } 346 347 y.color = x.parent.color; 348 x.parent.color = Node.BLACK; 349 y.left.color = Node.BLACK; 350 rightRotate(x.parent); 351 x = root; 352 } 353 } 354 } 355 x.color = Node.BLACK; 356 } 357 }
測試用例程序Main.java文件
1 package tree; 2 3 public class Main { 4 public static void main(String[] args) { 5 Rbtree tree = new Rbtree(); 6 7 tree.insert(12); 8 tree.insert(12); 9 tree.insert(2); 10 tree.insert(23); 11 tree.insert(1); 12 13 System.out.println(tree); 14 15 tree.remove(1); 16 tree.remove(2); 17 tree.remove(12); 18 tree.remove(12); 19 tree.remove(23); 20 System.out.println(tree); 21 } 22 }
參考資料:
1、《算法導論》第13章 紅黑樹