紅黑樹的原理和常見操作

1. 概述

在jdk1.8中,HashMap和ConcurrentHashMap中都采用了紅黑樹這一數(shù)據(jù)結(jié)構(gòu)。即當鏈表達到一定的長度后,就把鏈表轉(zhuǎn)化成紅黑樹。這其中主要利用了紅黑樹的良好性質(zhì),不管你節(jié)點怎樣,他始終保持查找時間復雜度為O(logn)。這樣的性質(zhì)相對于鏈表在長度很長的時候有很大的優(yōu)勢。O(logn)<O(lgn)

2. 紅黑樹的定義
  • 每個結(jié)點要么是紅的,要么是黑的。
  • 根結(jié)點是黑的。
  • 每個葉結(jié)點(葉結(jié)點即指樹尾端NIL指針或NULL結(jié)點)是黑的。
  • 如果一個結(jié)點是紅的,那么它的倆個兒子都是黑的。
  • 對于任一結(jié)點而言,其到葉結(jié)點樹尾端NIL指針的每一條路徑都包含相同數(shù)目的黑結(jié)點。
    一棵典型的紅黑樹如下:


    紅黑樹
3. 紅黑樹的插入

在這里,默認你熟悉二叉查找樹的結(jié)構(gòu),以及他的插入和刪除操作。紅黑樹本質(zhì)上是一棵二叉查找樹。
對紅黑樹的插入,我們先按二叉查找樹去插入,然后對對樹做調(diào)整,使其滿足紅黑樹的五條定義。(這是因為你插入會破壞其中的規(guī)則)
二叉查找樹的插入偽代碼如下:

RB-INSERT(T, z)  
 1  y ← nil[T]  
 2  x ← root[T]  
 3  while x ≠ nil[T]  
 4      do y ← x  
 5         if key[z] < key[x]  
 6            then x ← left[x]  
 7            else x ← right[x]  
 8  p[z] ← y  
 9  if y = nil[T]  
10     then root[T] ← z  
11     else if key[z] < key[y]  
12             then left[y] ← z  
13             else right[y] ← z  
14  left[z] ← nil[T]  
15  right[z] ← nil[T]  
16  color[z] ← RED  
17  RB-INSERT-FIXUP(T, z)  

可以看出,RB-INSERT(T, z)前面的第1-13行代碼基本就是二叉查找樹的插入代碼,然后第14-16行代碼把z的左孩子、右孩子都賦為葉結(jié)點nil,再把z結(jié)點著為紅色,最后為保證紅黑性質(zhì)在插入操作后依然保持,調(diào)用一個輔助程序RB-INSERT-FIXUP來對結(jié)點進行重新著色,并旋轉(zhuǎn)。
換言之

  • 如果插入的是根結(jié)點,因為原樹是空樹,此情況只會違反性質(zhì)2,所以直接把此結(jié)點涂為黑色。
  • 如果插入的結(jié)點的父結(jié)點是黑色,由于此不會違反性質(zhì)2和性質(zhì)4,紅黑樹沒有被破壞,所以此時也是什么也不做。

但當遇到下述3種情況時:

  • 插入修復情況1:如果當前結(jié)點的父結(jié)點是紅色且祖父結(jié)點的另一個子結(jié)點(叔叔結(jié)點)是紅色
  • 插入修復情況2:當前結(jié)點的父結(jié)點是紅色,叔叔結(jié)點是黑色,當前結(jié)點是其父結(jié)點的右子
  • 插入修復情況3:當前結(jié)點的父結(jié)點是紅色,叔叔結(jié)點是黑色,當前結(jié)點是其父結(jié)點的左子
3.1 插入修復情況1:當前結(jié)點的父結(jié)點是紅色且祖父結(jié)點的另一個子結(jié)點(叔叔結(jié)點)是紅色。

插入的偽代碼如下:

 1 while color[p[z]] = RED  
 2     do if p[z] = left[p[p[z]]]  
 3           then y ← right[p[p[z]]]  
 4                if color[y] = RED  
 5                   then color[p[z]] ← BLACK                    ? Case 1  
 6                        color[y] ← BLACK                       ? Case 1  
 7                        color[p[p[z]]] ← RED                   ? Case 1  
 8                        z ← p[p[z]]                            ? Case 1  

如下面兩個調(diào)整前的圖和調(diào)整之后的圖做對比。其中4為新添加的點。它的父節(jié)點為紅色,叔叔節(jié)點為紅色。那么調(diào)整就是把4的叔叔節(jié)點變?yōu)楹谏?節(jié)點),4的祖父節(jié)點變?yōu)榧t色,把當前結(jié)點指向祖父結(jié)點,從新的當前結(jié)點重新開始算法(7節(jié)點變?yōu)镹)。


3.1.1 調(diào)整前的樹

3.1.2 調(diào)整后的紅黑樹
3.2 插入修復情況2:當前結(jié)點的父結(jié)點是紅色,叔叔結(jié)點是黑色,當前結(jié)點是其父結(jié)點的右子
 9                   else if z = right[p[z]]
10                           then z ← p[z]                       ? Case 2
11                                LEFT-ROTATE(T, z)              ? Case 2

調(diào)整之前的圖如3.1.2,經(jīng)過調(diào)整后得到如下的圖。過程是當前結(jié)點的父結(jié)點做為新的當前結(jié)點,以新當前結(jié)點為支點左旋.


3.2.2 調(diào)整之后的樹
3.3 插入修復情況3:當前結(jié)點的父結(jié)點是紅色,叔叔結(jié)點是黑色,當前結(jié)點是其父結(jié)點的左子
12                           color[p[z]] ← BLACK                 ? Case 3
13                           color[p[p[z]]] ← RED                ? Case 3
14                           RIGHT-ROTATE(T, p[p[z]])            ? Case 3

調(diào)整過程是把父結(jié)點變?yōu)楹谏?,祖父結(jié)點變?yōu)榧t色,在祖父結(jié)點為支點右旋。最后,把根結(jié)點涂為黑色,整棵紅黑樹便重新恢復了平衡。調(diào)整之前的圖如3.2.2,經(jīng)過調(diào)整后得到下圖。


3.3.2調(diào)整后的圖
3. 紅黑樹的刪除

在這里默認你熟悉二叉查找樹的刪除操作。

下面我們用一個分析技巧:我們從被刪結(jié)點后來頂替它的那個結(jié)點開始調(diào)整,并認為它有額外的一重黑色。這里額外一重黑色是什么意思呢,我們不是把紅黑樹的結(jié)點加上除紅與黑的另一種顏色,這里只是一種假設(shè),我們認為我們當前指向它,因此空有額外一種黑色,可以認為它的黑色是從它的父結(jié)點被刪除后繼承給它的,它現(xiàn)在可以容納兩種顏色,如果它原來是紅色,那么現(xiàn)在是紅+黑,如果原來是黑色,那么它現(xiàn)在的顏色是黑+黑。有了這重額外的黑色,原紅黑樹性質(zhì)5就能保持不變?,F(xiàn)在只要恢復其它性質(zhì)就可以了,做法還是盡量向根移動和窮舉所有可能性。"--saturnman。
如果是以下情況,恢復比較簡單:

  • 當前結(jié)點是紅+黑色
    解法,直接把當前結(jié)點染成黑色,結(jié)束此時紅黑樹性質(zhì)全部恢復。
  • 當前結(jié)點是黑+黑且是根結(jié)點, 解法:什么都不做,結(jié)束。

下面4中特殊情況

  • 刪除修復情況1:當前結(jié)點是黑+黑且兄弟結(jié)點為紅色(此時父結(jié)點和兄弟結(jié)點的子結(jié)點分為黑)
  • 刪除修復情況2:當前結(jié)點是黑加黑且兄弟是黑色且兄弟結(jié)點的兩個子結(jié)點全為黑色
  • 刪除修復情況3:當前結(jié)點顏色是黑+黑,兄弟結(jié)點是黑色,兄弟的左子是紅色,右子是黑色
  • 刪除修復情況4:當前結(jié)點顏色是黑-黑色,它的兄弟結(jié)點是黑色,但是兄弟結(jié)點的右子是紅色,兄弟結(jié)點左子的顏色任意

4.1 刪除修復情況1:當前結(jié)點是黑+黑且兄弟結(jié)點為紅色(此時父結(jié)點和兄弟結(jié)點的子結(jié)點分為黑)。
解法:把父結(jié)點染成紅色,把兄弟結(jié)點染成黑色,之后重新進入算法(我們只討論當前結(jié)點是其父結(jié)點左孩子時的情況)。此變換后原紅黑樹性質(zhì)5不變,而把問題轉(zhuǎn)化為兄弟結(jié)點為黑色的情況(注:變化前,原本就未違反性質(zhì)5,只是為了把問題轉(zhuǎn)化為兄弟結(jié)點為黑色的情況)。

變化前

變化后

4.2 刪除修復情況2:當前結(jié)點是黑加黑且兄弟是黑色且兄弟結(jié)點的兩個子結(jié)點全為黑色。

解法:把當前結(jié)點和兄弟結(jié)點中抽取一重黑色追加到父結(jié)點上,把父結(jié)點當成新的當前結(jié)點,重新進入算法。(此變換后性質(zhì)5不變)。偽代碼如下。

//調(diào)用RB-DELETE-FIXUP(T, x) 的9-11行代碼
9                if color[left[w]] = BLACK and color[right[w]] = BLACK
10                   then color[w] ← RED                          ?  Case 2
11                        x p[x]                                  ?  Case 2

4.3 刪除修復情況3:當前結(jié)點顏色是黑+黑,兄弟結(jié)點是黑色,兄弟的左子是紅色,右子是黑色。
解法:把兄弟結(jié)點染紅,兄弟左子結(jié)點染黑,之后再在兄弟結(jié)點為支點解右旋,之后重新進入算法。此是把當前的情況轉(zhuǎn)化為情況4,而性質(zhì)5得以保持。偽代碼如下

//調(diào)用RB-DELETE-FIXUP(T, x) 的第12-16行代碼
12                   else if color[right[w]] = BLACK
13                           then color[left[w]] ← BLACK          ?  Case 3
14                                color[w] ← RED                  ?  Case 3
15                                RIGHT-ROTATE(T, w)              ?  Case 3
16                                w ← right[p[x]]                 ?  Case 3

4.4 刪除修復情況4:當前結(jié)點顏色是黑-黑色,它的兄弟結(jié)點是黑色,但是兄弟結(jié)點的右子是紅色,兄弟結(jié)點左子的顏色任意。
解法:把兄弟結(jié)點染成當前結(jié)點父結(jié)點的顏色,把當前結(jié)點父結(jié)點染成黑色,兄弟結(jié)點右子染成黑色,之后以當前結(jié)點的父結(jié)點為支點進行左旋,此時算法結(jié)束,紅黑樹所有性質(zhì)調(diào)整正確。代碼如下

//調(diào)用RB-DELETE-FIXUP(T, x) 的第17-21行代碼
17                         color[w] ← color[p[x]]                 ?  Case 4
18                         color[p[x]] ← BLACK                    ?  Case 4
19                         color[right[w]] ← BLACK                ?  Case 4
20                         LEFT-ROTATE(T, p[x])                   ?  Case 4
21                         x ← root[T]                            ?  Case 4

以上就是紅黑樹的結(jié)構(gòu)和性質(zhì),對于面試和源碼的理解很有幫助。

參考文章:
教你透徹了解紅黑樹

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一. 算法之變,結(jié)構(gòu)為宗 計算機在很多情況下被應(yīng)用于檢索數(shù)據(jù),比如航空和鐵路運輸業(yè)的航班信息和列車時刻表的查詢,都...
    Leesper閱讀 7,391評論 13 42
  • 原文鏈接 二叉查找樹 由于紅黑樹本質(zhì)上就是一棵二叉查找樹,所以在了解紅黑樹之前,咱們先來看下二叉查找樹。 二叉查找...
    非典型程序員閱讀 2,935評論 2 5
  • 數(shù)據(jù)結(jié)構(gòu)與算法--從平衡二叉樹(AVL)到紅黑樹 上節(jié)學習了二叉查找樹。算法的性能取決于樹的形狀,而樹的形狀取決于...
    sunhaiyu閱讀 7,807評論 4 32
  • 0.目錄 1.算法導論的紅黑樹本質(zhì)上是2-3-4樹 2.紅黑樹的結(jié)構(gòu)和性質(zhì) 3.紅黑樹的插入 4.紅黑樹的刪除 5...
    王偵閱讀 2,734評論 1 2
  • 1、紅黑樹介紹 紅黑樹又稱R-B Tree,全稱是Red-Black Tree,它是一種特殊的二叉查找樹,紅黑樹的...
    文哥的學習日記閱讀 10,137評論 1 35

友情鏈接更多精彩內(nèi)容