AVL自平衡二叉樹在教科書上比較常見,由於是最早提出的自平衡二叉樹,天然是學術價值比較的高,可是目前工業環境以爲名爲紅黑二叉樹RBT(Red-Black Tree)的自平衡二叉樹使用的更爲的普遍,好比C++標準庫中的有序容器(std::set、std::map),Linux內核中的不少數據結構等,都是用的RBTree來維護管理的。java
當看完RBTree後發現,其實相對來講AVL自平衡樹比RBTree更加的平衡,理論上訪問效果也會更好,可是爲此AVL自平衡樹在插入、刪除修改樹結構的時候,會引入更多的旋轉操做來保持平衡,因此對於常常須要添加、刪除的高動態數據來講,維護這種數據結構的代價顯得十分高昂,而RBTree對於樹的高度限制相對要寬鬆的多,等因而在犧牲了部分徹底性(平衡性)的條件下,以換取插入、刪除操做時少許的旋轉操做(可是一調整起來複雜的狀況麻煩的要死~~~)。node
1、紅黑二叉樹簡介數據結構
說到紅黑二叉樹,不得不先請出紅黑樹的基本法則,雖然簡單,可是維護起來仍是挺複雜的:app
(1). 節點都有顏色標記,且只能是紅色或黑色。ide
(2). 根是黑色。性能
(3). 全部葉子都是黑色(葉子是NIL/nill_節點,不保存實際的數據)。ui
(4). 每一個紅色節點必須有兩個黑色的子節點,也能夠表述爲從每一個葉子到根的全部路徑上不能有兩個連續的紅色節點。spa
(5). 從任一節點到其每一個葉子的全部簡單路徑都包含相同數目的黑色節點。3d
上述這些條件中,(1)(3)是很容易遵照的,(2)只有確實在操做到根節點的時候須要注意調整一下就好了,而(4)(5)是維持紅黑樹結構中經常會用到的準則。指針
之因此要有上面的條件和規則,就是爲了這麼一個保證:從根到任何一個葉子,最好的狀況是整條路徑所有都是黑色,假設爲N,最壞的狀況是黑色和紅色交替的狀況,那也不會超過2N,所以紅黑二叉樹對操做的複雜度做出了最差的保證。而維護這種數據結構,須要少許的顏色變動和不超過三次樹旋轉(對於插入操做最可能是兩次),雖然插入和刪除很複雜,但操做時間複雜度仍能夠保持爲O(log n)而不會由於元素的數目增長而性能惡化。下面是一個典型的紅黑二叉樹的樣子。
2、紅黑二叉樹實現
此處還須要事先強調一下,紅黑樹再複雜也是一個基本的BST,因此和AVL自平衡二叉樹同樣,全部的插入和刪除操做,也是先按照操做BST的基本方式進行插入、刪除,而後再檢查是否平衡而作出相應調整,RBTree的調整操做包括着色重繪和旋轉操做。
2.1 插入操做
規定,新增長的節點着色必須是紅色。插入操做分如下狀況:
(1). 若是此時仍是空的紅黑樹,則該節點爲根節點,將其重繪爲黑色,而後返回;不然進行步驟(2)
(2). 根據BST遞歸查找,找到能夠插入的位置時候,建立新節點後進行BST插入(更新parent_、left_、right_指針域),而後進行步驟(3)
(3). 若是父親節點P是黑色,此時紅色節點做爲孩子是容許的,紅色節點的加入不會影響黑色路徑的計數,原先父親的葉子節點黑色由新插入節點的葉子節點繼承,對於(4)(5)規則沒有任何影響,操做完成直接返回;不然進行步驟(4)
(4). 父親節P點是紅色的,若是此時叔父節點U也是紅色的,並且此刻肯定祖父節點G是黑色的,進行以下操做:將祖父節點G重繪爲紅色,將父親P和叔父節U點重繪爲黑色,此處操做雖然子樹平衡了,可是修改了祖父節點G可能致使祖父節點G和其餘節點不平衡,以祖父節點G爲參數從新進入步驟(3)檢查遞歸;不然進行(5)
(5). 此時父節點P是紅色、叔父節點U是黑色、祖父節點G是黑色、新增節點N是紅色,根據插入節點N是父親節點G的左孩子仍是右孩子,以及父親節點P是祖父節點G的左孩子仍是右孩子分別組合,一共造成四種狀況,依次處理
a. 若是祖父節點G、父親節點P、插入節點N在一條直線上,即插入節點N是父親節點P的左孩子且父節點P是祖父節點G的左孩子,或者鏡像狀況下插入節點N是父親節點P的右孩子且父親節點P是祖父節點G的右孩子,此時只需將祖父節點G改成紅色,父親節點P改成黑色,而後以祖父親節點G爲中心作一個相反方向的旋轉就能夠了;
b. 若是祖父節點G、父親節點P、插入節點N不在一條直線上,此時須要以父親節點P爲中心,事先進行一次旋轉使得祖父節點、父親節點、插入節點三者在一條直線上,而後就能夠按照a的狀況所對應的步驟進行處理了;
至此,紅黑樹的插入操做完成。
2.2 刪除操做
紅黑樹的刪除操做算是比較複雜的數據結構操做了,分出的狀況比較的多,並且操做過程當中涉及到的親戚節點也比較多。
此處須要說明的是,全部BST的刪除操做,均可以轉換看做是單個子樹的刪除操做,由於刪除的節點只可能有三種狀況:葉子節點,這時候能夠將任意一個NIL節點看作單個子樹;含有一個子樹的節點;含有兩個子樹的節點,此時按照BST的刪除操做,仍是會尋找一個左子樹最大值或者右子樹最小值替換他,並將替換節點重繪成刪除節點的顏色,而後問題實質上就轉化成替換節點的刪除了,而替換節點不可能有兩個子樹的狀況。
(1). 若是整個RBTree爲空,直接返回;不然進入(2)
(2). 查找當前節點是否待刪除節點,若是不是遞歸進行左樹或者右樹刪除,若是遍歷完了還未找到直接返回,不然當前就是待刪除節點,進入步驟(3)
(3). 若是當前待刪除節點的右子樹爲空,代表沒法找到一個用於替換的右子樹最小值節點,直接進入步驟(4),不然查找右子數最小節點,和當前節點進行數據部分的交換(而位置關係、着色得以保留),而後將待刪除節點設置爲替換節點,進入步驟(4),至此咱們找到了須要真正進行刪除操做的節點N
(4). 尋找當前刪除節點node的非NIL子樹(若是當前節點兩個孩子都是NIL那就選隨便選一個假設爲非NIL)做爲child,而後判斷當前節點是不是根節點而須要作特殊處理(此處不展開這種狀況,只考慮通常的刪除節點),對於通常的刪除狀況,經過將node父節點指針引用到child而使用child頂替當前刪除節點node,node的引用計數減小(後續會被自動析構釋放),並且此時若是刪除掉的節點node是紅色的,那麼代表被刪除節點的父親和孩子child都是黑色的,最主要的是刪除一個紅色節點不影響RBTree的規則,此處就能夠直接返回了;不然進入步驟(5)
(5). 此處刪除掉的節點node是黑色的,而若是替換的child是紅色的,那麼將孩子重繪爲黑色,這樣原來經過黑色刪除節點的路線如今都由孩子去做爲黑色節點頂替了,紅黑樹的特性沒有被破壞,直接返回;不然進入步驟(6)
(6). 進入步驟(6)就是傳說中最複雜的double black狀況了,在全部討論以前這裏先聲明,到達這裏節點的刪除工做已經完成,接下來的都是調整工做了。咱們將主角命名爲N,其本質就是(4)操做中頂替的child節點,原先的祖父節點如今是父親節點,原先的叔父節點如今是兄弟節點,且此時節點N是黑色的。檢測兄弟節點S若是是紅色,那麼父親節點P確定是黑色,此時重繪父親節點P成紅色,重繪兄弟節點S爲黑,而且若是N是父親節點P的左兒子,則以父親節點P爲中心進行左旋操做,不然進行右旋操做,通過這一步調整,N有了一個黑色的兄弟節點和一個紅色的父親節點,可是N和如今兄弟節點S原來的兒子(如今的兄弟)掛着子樹上黑色節點的數目確定不同,子樹是不平衡的,因此還須要繼續進行下面的處理,進入步驟(7)。
(7). 不管是原生的仍是通過步驟(6)修改獲得的,此處兄弟節點S是黑色、N是黑色,而後檢查兄弟節點S的兩個孩子的顏色,若是兄弟節點S的兩個孩子都是黑色,那麼就根據父親節點P的顏色進行討論:
a.若是此時父親節點P的顏色是紅色,則重繪兄弟節點S成紅色、重繪父親節點P成黑色,經過這樣後原先刪除掉的黑色節點就由父親節點P補償回來了,而兄弟節點S的整個分支沒有改變,知足紅黑樹條件,就直接返回;
b.若是父親節點P的顏色是黑色,則重繪兄弟節點S成紅色,此時雖然獲得的整個子樹是平衡的,可是原先通過兄弟節點S的子樹都減小了一個黑色,此處須要以父親節點P爲參數步入步驟(4)從新調整;
若是兄弟節點S的兩個孩子不都是黑色,此時步入步驟(8)
(8). 此時兄弟節點S是黑色、N是黑色,並且兄弟節點S的兩個孩子至少有一個是紅色的,可是父親節點P屢次遞歸已經不肯定顏色了,而後當Parnet-Sibling-r_child不在一條線上面時(此時兄弟節點S的孩子由一個紅色和一個黑色構成的時候,假設紅色孩子記爲r_child),須要先旋轉成一條線,同時進行顏色的修正,把兄弟節點S改爲紅色且r_child改爲黑色,通過這個旋轉後的子樹是知足二叉樹性質的,可是N和新的兄弟節點S不平衡(自己這個操做不會涉及到N和父親節點P),並且這個不平衡的狀況恰好會fall through到下面步驟(9)的狀況處理;而若是Parnet-Sibling-r_child在一條線上面(這其實就是前面旋轉着色後的結果),直接進入步驟(9)處理
(9). 此時兄弟節點S是黑色,且依次掛了紅色孩子、黑色孫子一條線的子樹,操做是經過以父親節點P爲中心進行旋轉,讓原來的兄弟節點S代替父親節點P的顏色,同時重繪原來父親節點P成黑色,重繪原來兄弟節點S的孩子成黑色。
因爲原先的父親節點P和如今兄弟節點S的顏色是不肯定的,無非作兩種狀況進行討論:a. 父親節點P原先是黑色的;b. 父親節點P原先是紅色的,看圖能夠直接分析出來,修改以後這條子樹到其全部子葉的黑色節點數目和原先都是同樣的,知足紅黑樹條件,刪除結束。
3、代碼實現
1 import java.util.List; 2 3 /** 4 * 紅黑樹 <br/> 5 * 1)每一個節點要麼是紅色要麼是黑色 2)根節點是黑色的 3)每一個葉子節點(NIL)是黑色 4)紅色節點的的子節點都是黑色的 6 * 5)對每一個節點,從該節點到其後代葉子節點的簡單路徑上,均包含數目相同的黑色節點 7 * 8 * 一般咱們認爲樹末梢的節點還有兩個爲空的節點,這些空節點是黑色的,因此沒必要檢測第三條 9 * 10 */ 11 public class RedBlackTree<K, V> extends BinarySearchTree<K, V> { 12 private static final boolean RED = true; 13 private static final boolean BLACK = false; 14 15 @Override 16 public BSTNode<K, V> insert(K key, V value) { 17 BSTNode<K, V> newNode = super.insert(key, value); 18 fixAfterInsert2(newNode.parent, newNode); 19 colorBlack(root);// 根節點染黑 20 size++; 21 return newNode; 22 } 23 24 /** 25 * 保持樹的平衡,這裏採用模式匹配的寫法: y x z A B C D 26 * 27 * @param parent 新增節點的父節點 28 * @param newNode 新增節點 29 * @return 30 */ 31 private void fixAfterInsert(BSTNode parent, BSTNode newNode) { 32 if (parent == null) { 33 root = newNode; 34 return; 35 } 36 if (colorOf(parent) == RED && colorOf(newNode) == RED) { 37 // 虛位以待,把四種狀況的ABC和xyz定好,而後統一處理 38 BSTNode A = null, B = null, C = null, D = null, x = null, y = null, z = null; 39 // case 1 左左 40 /* 41 * 42 * z y D x C A B 43 */ 44 if (parent.isLeftChild && newNode.isLeftChild) { 45 x = newNode; 46 A = x.left; 47 B = x.right; 48 49 y = parent; 50 C = y.right; 51 52 z = y.parent; 53 D = z.right; 54 55 changePeek(y, z); 56 } 57 // case 2 右右 58 /* 59 * 60 * x A y B z C D --> A x B y C z D 61 */ 62 else if (!parent.isLeftChild && !newNode.isLeftChild) { 63 z = newNode; 64 C = z.left; 65 D = z.right; 66 67 y = z.parent; 68 B = y.left; 69 70 x = y.parent; 71 A = x.left; 72 73 changePeek(y, x); 74 } 75 // case 3 左右 76 else if (parent.isLeftChild && !newNode.isLeftChild) { 77 y = newNode; 78 B = y.left; 79 C = y.right; 80 81 x = y.parent; 82 A = x.left; 83 84 z = x.parent; 85 D = z.right; 86 87 changePeek(y, z); 88 } 89 // case 4 右左 90 else if (!parent.isLeftChild && newNode.isLeftChild) { 91 y = newNode; 92 B = y.left; 93 C = y.right; 94 95 z = y.parent; 96 D = z.right; 97 98 x = z.parent; 99 A = x.left; 100 101 changePeek(y, x); 102 } 103 // ------------------統一變爲一種形式,換父子連接並染色---------------------- 104 x.parent = y; 105 z.parent = y; 106 y.left = x; 107 y.right = z; 108 x.left = A; 109 if (A != null) { 110 A.parent = x; 111 A.isLeftChild = true; 112 } 113 x.right = B; 114 if (B != null) { 115 B.parent = x; 116 B.isLeftChild = false; 117 } 118 z.left = C; 119 z.right = D; 120 if (C != null) { 121 C.parent = z; 122 C.isLeftChild = true; 123 } 124 if (D != null) { 125 D.parent = z; 126 D.isLeftChild = false; 127 } 128 x.isLeftChild = true; 129 z.isLeftChild = false; 130 colorBlack(x); 131 colorBlack(z); 132 colorRed(y); 133 // 遞歸向上追溯 134 fixAfterInsert(y.parent, y); 135 } 136 137 } 138 139 private void fixAfterInsert2(BSTNode parent, BSTNode newNode) { 140 if (parent == null) { 141 root = newNode; 142 return; 143 } 144 if (colorOf(parent) == RED) { 145 // uncle存在且爲紅色 146 BSTNode grand = parent.parent; 147 BSTNode uncle = parent.isLeftChild ? grand.right : grand.left; 148 // uncle爲紅 149 if (uncle != null && colorOf(uncle) == RED) { 150 colorRed(grand); 151 colorBlack(parent); 152 colorBlack(uncle); 153 fixAfterInsert2(grand.parent, grand); 154 } else {// uncle爲空=uncle爲黑 155 if (parent.isLeftChild && newNode.isLeftChild) {// 左左型 156 colorRed(grand); 157 colorBlack(parent); 158 rightRotate(grand, parent); 159 } else if (parent.isLeftChild && !newNode.isLeftChild) {// 左右型 160 leftRotate(parent, newNode); 161 colorRed(grand); 162 colorBlack(newNode); 163 rightRotate(grand, newNode); 164 } else if (!parent.isLeftChild && !newNode.isLeftChild) {// 右右型 165 colorRed(grand); 166 colorBlack(parent); 167 leftRotate(grand, parent); 168 } else {// 右左型 169 rightRotate(parent, newNode); 170 colorRed(grand); 171 colorBlack(newNode); 172 leftRotate(grand, newNode); 173 } 174 } 175 } 176 } 177 178 /** 179 * 切換頂點,設施newPeek爲新頂點 180 * 181 * @param newPeek 182 * 新頂點 183 * @param oldPeek 184 * 舊頂點 185 */ 186 private void changePeek(BSTNode newPeek, BSTNode oldPeek) { 187 newPeek.parent = oldPeek.parent; 188 newPeek.isLeftChild = oldPeek.isLeftChild; 189 if (oldPeek.parent != null) { 190 if (oldPeek.isLeftChild) 191 oldPeek.parent.left = newPeek; 192 else 193 oldPeek.parent.right = newPeek; 194 } else { 195 root = newPeek; 196 } 197 } 198 199 private void colorRed(BSTNode node) { 200 if (null != node) 201 node.isRed = true; 202 } 203 204 private void colorBlack(BSTNode node) { 205 if (null != node) 206 node.isRed = false; 207 } 208 209 /** 210 * 紅黑樹刪除及修復 一、雙支轉單支 二、刪除D,並頂替N 三、D爲黑,需修復 四、N爲紅,很簡單(N變黑便可) N爲黑,系列複雜的修復 211 * 212 * @param key 213 */ 214 @Override 215 public void remove(K key) { 216 BSTNode toDelete = lookupNode(key); 217 if (toDelete == null) 218 return; 219 size--; 220 221 // 若是是嚴格的內部節點,拷貝後繼元素的內容到待刪節點,而後toDelete指向後繼,合併到後面一同處理 222 if (toDelete.left != null && toDelete.right != null) { 223 BSTNode s = successor(toDelete);// 後繼右子樹的最左端 224 toDelete.key = s.key; 225 toDelete = s; // p指向其後繼,是待刪除的 226 } // toDelete has 2 children 227 228 // 此時,toDelete必定是單支,或者是葉子 229 // 用於頂替待刪節點的 230 BSTNode N = (toDelete.left != null ? toDelete.left : toDelete.right); 231 // N是用來頂替toDelete的 232 if (N != null) { 233 // -------這一段是頂替操做----------- 234 // Link N to parent 235 N.parent = toDelete.parent; 236 if (toDelete.parent == null) { 237 root = N; 238 colorBlack(N); 239 } else if (toDelete.isLeft()) { // p是左孩子 240 toDelete.parent.left = N; 241 N.isLeftChild = true; 242 } else { // p是右孩子 243 toDelete.parent.right = N; 244 N.isLeftChild = false; 245 } 246 247 // Null out links so they are OK to use by fixAfterDeletion. 248 toDelete.left = toDelete.right = toDelete.parent = null; 249 // -------這一段是頂替操做 end----------- 250 251 // toDelete爲黑才須要修復 252 if (colorOf(toDelete) == BLACK) 253 fixAfterDeletion(N); 254 } // toDelete has 1 children 255 else if (toDelete.parent == null) { // toDelete是葉子:1.它是根—— if it is the 256 // only node. 257 root = null;// 變成空樹 258 } else { // toDelete是葉子:2.不是根,沒有頂替者. 259 // toDelete爲黑才須要修復 260 if (colorOf(toDelete) == BLACK) 261 fixAfterDeletion(toDelete);// 先修復再cut掉 262 263 // 如下代碼執行切掉葉子的動做 264 if (toDelete.parent != null) { 265 if (toDelete == toDelete.parent.left) 266 toDelete.parent.left = null; 267 else if (toDelete == toDelete.parent.right) 268 toDelete.parent.right = null; 269 toDelete.parent = null; 270 } 271 } 272 273 } 274 275 private void fixAfterDeletion(BSTNode N) { 276 if (colorOf(N) == RED) {// N爲紅,簡單變黑便可 277 colorBlack(N); 278 } 279 // N爲黑,即double black,刪除節點和頂替節點都爲黑,進行若干種狀況的討論 280 // case1:N是新的根節點,且N爲黑色,沒有任何破壞 281 else if (N.parent == null) { 282 } else {// 爲黑,且不是根節點 283 case2(N); 284 } 285 } 286 287 /*-------狀況2:兄弟爲紅色,轉移爲兄弟爲黑-------*/ 288 private void case2(BSTNode N) { 289 BSTNode parent = N.parent; 290 BSTNode sib = sib(N, parent); 291 if (colorOf(sib) == RED) { 292 colorBlack(sib); 293 colorRed(parent); 294 if (N.isLeft()) 295 leftRotate(parent, N); 296 else 297 rightRotate(parent, N); 298 } 299 case3(N);// sib must be black. 300 } 301 302 private BSTNode sib(BSTNode N, BSTNode parent) { 303 BSTNode sib; 304 if (N.isLeft()) { 305 sib = parent.right; 306 } else { 307 sib = parent.left; 308 } 309 return sib; 310 } 311 312 /*-------狀況3:兄弟爲黑的前提下,討論兄弟的雙子爲黑(兄弟能夠被染紅) 313 * 1.父爲紅色,雙子爲黑或者不爲黑都走向case4 314 * 2.父爲黑色,兄弟染紅,遞歸父節點*/ 315 private void case3(BSTNode N) { 316 BSTNode parent = N.parent; 317 BSTNode sib = sib(N, parent); 318 /* 2.父爲黑色,兄弟染紅,遞歸父節點 */ 319 if (colorOf(parent) == BLACK && (sib.left == null || colorOf(sib.left) == BLACK) 320 && (sib.right == null || colorOf(sib.right) == BLACK)) { 321 colorRed(sib); 322 fixAfterDeletion(parent); 323 } else { 324 case4(N); 325 } 326 } 327 328 /*-------狀況4.1:P爲紅,兄弟爲黑,且兄弟的雙子爲黑——父兄反色,便可 329 * 4.2 P紅或者黑,兄弟爲黑,不管哪一子爲紅,都轉移到case5*/ 330 private void case4(BSTNode N) { 331 BSTNode parent = N.parent; 332 BSTNode sib = sib(N, parent); 333 if (colorOf(parent) == RED && colorOf(sib.left) == BLACK && colorOf(sib.right) == BLACK) { 334 colorRed(sib); 335 colorBlack(parent);// 恰好平衡 336 } else { 337 case5(N); 338 } 339 } 340 341 /*-------狀況5,兄弟向內的孩子爲紅,經過旋轉轉移爲case6:向外的孩子爲紅*/ 342 private void case5(BSTNode N) { 343 BSTNode parent = N.parent; 344 BSTNode sib = sib(N, parent); 345 if (N.isLeft() && colorOf(sib.right) == BLACK) { 346 // s->color = RED; 347 // s->left->color = BLACK; 348 // rotate_right(s); 349 colorBlack(sib.left); 350 colorRed(sib); 351 rightRotate(sib, sib.left);// 兄弟的外側孩子變爲紅色 352 } else if (N.isRight() && colorOf(sib.left) == BLACK) { 353 colorRed(sib); 354 colorBlack(sib.right); 355 leftRotate(sib, sib.right); 356 } 357 case6(N); 358 } 359 360 /*-------狀況6兄弟向外的孩子爲紅 361 * 兄弟染爲父節點的顏色 362 * 父節點染黑 363 * 父節點旋轉*/ 364 private void case6(BSTNode N) { 365 BSTNode parent = N.parent; 366 BSTNode sib = sib(N, parent); 367 setColor(sib, colorOf(parent)); 368 colorBlack(parent); 369 if (N.isLeft()) { 370 colorBlack(sib.right); 371 leftRotate(parent, sib); 372 } else { 373 colorBlack(sib.left); 374 rightRotate(parent, sib); 375 } 376 } 377 378 private void setColor(BSTNode sib, boolean colorOf) { 379 if (sib != null) 380 sib.isRed = colorOf; 381 } 382 383 private BSTNode rightOf(BSTNode parent) { 384 return parent.right; 385 } 386 387 private BSTNode leftOf(BSTNode parent) { 388 return parent.left; 389 } 390 391 private boolean colorOf(BSTNode x) { 392 if (x == null) 393 return false; 394 return x.isRed; 395 } 396 397 @Override 398 public String toString() { 399 StringBuilder sb = new StringBuilder(); 400 List<List<BSTNode<K, V>>> lists = super.levelOrder(); 401 for (List<BSTNode<K, V>> l : lists) { 402 for (BSTNode<K, V> n : l) { 403 sb.append(n.toString() + "\t"); 404 } 405 sb.append("\n"); 406 } 407 return sb.toString(); 408 } 409 }