紅黑樹

  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 }
View Code

  原文連接:http://www.sohu.com/a/131380870_479559

相關文章
相關標籤/搜索