30張圖帶你完全理解紅黑樹

本文轉自安卓大叔算法

寫在前面

當在10億數據中只須要進行10幾回比較就能查找到目標時,不由感嘆編程之魅力!人類之偉大呀! —— 學紅黑樹有感。編程

終於,在學習了幾天的紅黑樹相關的知識後,我想把我所學所想和所感分享給你們。紅黑樹是一種比較難的數據結構,要徹底搞懂很是耗時耗力,紅黑樹怎麼自平衡?何時須要左旋或右旋?插入和刪除破壞了樹的平衡後怎麼處理?等等一連串的問題在學習前困擾着我。若是你在學習過程當中也會存在個人疑問,那麼本文對你會有幫助,本文幫助你全面、完全地理解紅黑樹!數據結構

本文將經過圖文的方式講解紅黑樹的知識點,而且不會涉及到任何代碼,相信我,在懂得紅黑樹實現原理前,看代碼會一頭霧水的,當原理懂了,代碼也就循序漸進寫而已,沒任何難度。學習

閱讀本文你需具有知識點:網站

  • 二叉查找樹
  • 完美平衡二叉樹

事不宜遲,讓咱們進入正題吧。3d



正文

紅黑樹也是二叉查找樹,咱們知道,二叉查找樹這一數據結構並不難,而紅黑樹之因此難是難在它是自平衡的二叉查找樹,在進行插入和刪除等可能會破壞樹的平衡的操做時,須要從新自處理達到平衡狀態。如今在腦海想下怎麼實現?是否是太多情景須要考慮了?嘖嘖,先別急,經過本文的學習後,你會以爲,其實也不過如此而已。好吧,咱們先來看下紅黑樹的定義和一些基本性質。blog

紅黑樹定義和性質

紅黑樹是一種含有紅黑結點並能自平衡的二叉查找樹。它必須知足下面性質:get

  • 性質1:每一個節點要麼是黑色,要麼是紅色。
  • 性質2:根節點是黑色。
  • 性質3:每一個葉子節點(NIL)是黑色。
  • 性質4:每一個紅色結點的兩個子結點必定都是黑色。
  • 性質5:任意一結點到每一個葉子結點的路徑都包含數量相同的黑結點。

從性質5又能夠推出:源碼

  • 性質5.1:若是一個結點存在黑子結點,那麼該結點確定有兩個子結點

圖1就是一顆簡單的紅黑樹。其中Nil爲葉子結點,而且它是黑色的。(值得提醒注意的是,在Java中,葉子結點是爲null的結點。io

 圖1 一顆簡單的紅黑樹

紅黑樹並非一個完美平衡二叉查找樹,從圖1能夠看到,根結點P的左子樹顯然比右子樹高,但左子樹和右子樹的黑結點的層數是相等的,也即任意一個結點到到每一個葉子結點的路徑都包含數量相同的黑結點(性質5)。因此咱們叫紅黑樹這種平衡爲黑色完美平衡

介紹到此,爲了後面講解不至於混淆,咱們還須要來約定下紅黑樹一些結點的叫法,如圖

圖2 結點叫法約定

咱們把正在處理(遍歷)的結點叫作當前結點,如圖2中的D,它的父親叫作父結點,它的父親的另一個子結點叫作兄弟結點,父親的父親叫作祖父結點。

前面講到紅黑樹能自平衡,它靠的是什麼?三種操做:左旋、右旋和變色。

  • 左旋:以某個結點做爲支點(旋轉結點),其右子結點變爲旋轉結點的父結點,右子結點的左子結點變爲旋轉結點的右子結點,左子結點保持不變。如圖3。
  • 右旋:以某個結點做爲支點(旋轉結點),其左子結點變爲旋轉結點的父結點,左子結點的右子結點變爲旋轉結點的左子結點,右子結點保持不變。如圖4。
  • 變色:結點的顏色由紅變黑或由黑變紅

圖3 左旋

圖4 右旋

上面所說的旋轉結點也即旋轉的支點,圖4和圖5中的P結點。
咱們先忽略顏色,能夠看到旋轉操做不會影響旋轉結點的父結點,父結點以上的結構仍是保持不變的。
左旋隻影響旋轉結點和其右子樹的結構,把右子樹的結點往左子樹挪了。
右旋隻影響旋轉結點和其左子樹的結構,把左子樹的結點往右子樹挪了。

因此旋轉操做是局部的。另外能夠看出旋轉能保持紅黑樹平衡的一些端詳了:當一邊子樹的結點少了,那麼向另一邊子樹「借」一些結點;當一邊子樹的結點多了,那麼向另一邊子樹「租」一些結點。

但要保持紅黑樹的性質,結點不能亂挪,還得靠變色了。怎麼變?具體情景又不一樣變法,後面會具體講到,如今只須要記住紅黑樹老是經過旋轉和變色達到自平衡

balabala了這麼多,相信你對紅黑樹有必定印象了,那麼如今來考考你:

思考題1:黑結點能夠同時包含一個紅子結點和一個黑子結點嗎? (答案見文末)

接下來先講解紅黑樹的查找熱熱身。


紅黑樹查找

由於紅黑樹是一顆二叉平衡樹,而且查找不會破壞樹的平衡,因此查找跟二叉平衡樹的查找無異:

  1. 從根結點開始查找,把根結點設置爲當前結點;
  2. 若當前結點爲空,返回null;
  3. 若當前結點不爲空,用當前結點的key跟查找key做比較;
  4. 若當前結點key等於查找key,那麼該key就是查找目標,返回當前結點;
  5. 若當前結點key大於查找key,把當前結點的左子結點設置爲當前結點,重複步驟2;
  6. 若當前結點key小於查找key,把當前結點的右子結點設置爲當前結點,重複步驟2;

如圖5所示。

圖5 二叉樹查找流程圖

很是簡單,但簡單不表明它效率很差。正因爲紅黑樹總保持黑色完美平衡,因此它的查找最壞時間複雜度爲O(2lgN),也即整顆樹恰好紅黑相隔的時候。能有這麼好的查找效率得益於紅黑樹自平衡的特性,而這背後的付出,紅黑樹的插入操做功不可沒~


紅黑樹插入

插入操做包括兩部分工做:一查找插入的位置;二插入後自平衡。查找插入的父結點很簡單,跟查找操做區別不大:

  1. 從根結點開始查找;
  2. 若根結點爲空,那麼插入結點做爲根結點,結束。
  3. 若根結點不爲空,那麼把根結點做爲當前結點;
  4. 若當前結點爲null,返回當前結點的父結點,結束。
  5. 若當前結點key等於查找key,那麼該key所在結點就是插入結點,更新結點的值,結束。
  6. 若當前結點key大於查找key,把當前結點的左子結點設置爲當前結點,重複步驟4;
  7. 若當前結點key小於查找key,把當前結點的右子結點設置爲當前結點,重複步驟4;

如圖6所示。

 
 
圖6 紅黑樹插入位置查找

ok,插入位置已經找到,把插入結點放到正確的位置就能夠啦,但插入結點是應該是什麼顏色呢?答案是紅色。理由很簡單,紅色在父結點(若是存在)爲黑色結點時,紅黑樹的黑色平衡沒被破壞,不須要作自平衡操做。但若是插入結點是黑色,那麼插入位置所在的子樹黑色結點老是多1,必須作自平衡。

全部插入情景如圖7所示。

 
圖7 紅黑樹插入情景

嗯,插入情景不少呢,8種插入情景!但情景一、2和3的處理很簡單,而情景4.2和情景4.3只是方向反轉而已,懂得了一種情景就能推出另一種情景,因此整體來看,並不複雜,後續咱們將一個一個情景來看,把它完全搞懂。

另外,根據二叉樹的性質,除了情景2,全部插入操做都是在葉子結點進行的。這點應該不難理解,由於查找插入位置時,咱們就是在找子結點爲空的父結點的。

在開始每一個情景的講解前,咱們仍是先來約定下,如圖8所示。

 
圖8 插入操做結點的叫法約定

圖8的字母並不表明結點Key的大小。I表示插入結點,P表示插入結點的父結點,S表示插入結點的叔叔結點,PP表示插入結點的祖父結點。

好了,下面讓咱們一個一個來分析每一個插入的情景以其處理。

插入情景1:紅黑樹爲空樹

最簡單的一種情景,直接把插入結點做爲根結點就行,但注意,根據紅黑樹性質2:根節點是黑色。還須要把插入結點設爲黑色。

處理:把插入結點做爲根結點,並把結點設置爲黑色

插入情景2:插入結點的Key已存在

插入結點的Key已存在,既然紅黑樹總保持平衡,在插入前紅黑樹已是平衡的,那麼把插入結點設置爲將要替代結點的顏色,再把結點的值更新就完成插入。

處理:

  • 把I設爲當前結點的顏色
  • 更新當前結點的值爲插入結點的值
插入情景3:插入結點的父結點爲黑結點

因爲插入的結點是紅色的,當插入結點的黑色時,並不會影響紅黑樹的平衡,直接插入便可,無需作自平衡。

處理:直接插入

插入情景4:插入結點的父結點爲紅結點

再次回想下紅黑樹的性質2:根結點是黑色。若是插入的父結點爲紅結點,那麼該父結點不可能爲根結點,因此插入結點老是存在祖父結點。這點很重要,由於後續的旋轉操做確定須要祖父結點的參與。

情景4又分爲不少子情景,下面將進入重點部分,各位看官請留神了。

插入情景4.1:叔叔結點存在而且爲紅結點
從紅黑樹性質4能夠,祖父結點確定爲黑結點,由於不能夠同時存在兩個相連的紅結點。那麼此時該插入子樹的紅黑層數的狀況是:黑紅紅。顯然最簡單的處理方式是把其改成:紅黑紅。如圖9和圖10所示。

處理:

  • 將P和S設置爲黑色
  • 將PP設置爲紅色
  • 把PP設置爲當前插入結點
 
圖9 插入情景4.1_1
 
圖10 插入情景4.1_2

能夠看到,咱們把PP結點設爲紅色了,若是PP的父結點是黑色,那麼無需再作任何處理;但若是PP的父結點是紅色,根據性質4,此時紅黑樹已不平衡了,因此還須要把PP看成新的插入結點,繼續作插入操做自平衡處理,直到平衡爲止。

試想下PP恰好爲根結點時,那麼根據性質2,咱們必須把PP從新設爲黑色,那麼樹的紅黑結構變爲:黑黑紅。換句話說,從根結點到葉子結點的路徑中,黑色結點增長了。這也是惟一一種會增長紅黑樹黑色結點層數的插入情景

咱們還能夠總結出另一個經驗:紅黑樹的生長是自底向上的。這點不一樣於普通的二叉查找樹,普通的二叉查找樹的生長是自頂向下的。

插入情景4.2:叔叔結點不存在或爲黑結點,而且插入結點的父親結點是祖父結點的左子結點
單純從插入前來看,也即不算情景4.1自底向上處理時的狀況,叔叔結點非紅即爲葉子結點(Nil)。由於若是叔叔結點爲黑結點,而父結點爲紅結點,那麼叔叔結點所在的子樹的黑色結點就比父結點所在子樹的多了,這不知足紅黑樹的性質5。後續情景一樣如此,再也不多作說明了。

前文說了,須要旋轉操做時,確定一邊子樹的結點多了或少了,須要租或借給另外一邊。插入顯然是多的狀況,那麼把多的結點租給另外一邊子樹就能夠了。

插入情景4.2.1:插入結點是其父結點的左子結點
處理:

  • 將P設爲黑色
  • 將PP設爲紅色
  • 對PP進行右旋
圖11 插入情景4.2.1

由圖11可得,左邊兩個紅結點,右邊不存在,那麼一邊一個剛恰好,而且由於爲紅色,確定不會破壞樹的平衡。

咦,能夠把PP設爲紅色,I和P設爲黑色嗎?答案是能夠!看過《算法:第4版》的同窗可能知道,書中講解的就是把PP設爲紅色,I和P設爲黑色。但把PP設爲紅色,顯然又會出現情景4.1的狀況,須要自底向上處理,作多了無謂的操做,既然能本身消化就不要麻煩祖輩們啦~

插入情景4.2.2:插入結點是其父結點的右子結點
這種情景顯然能夠轉換爲情景4.2.1,如圖12所示,不作過多說明了。

處理:

  • 對P進行左旋
  • 把P設置爲插入結點,獲得情景4.2.1
  • 進行情景4.2.1的處理
 
圖12 插入情景4.2.2

插入情景4.3:叔叔結點不存在或爲黑結點,而且插入結點的父親結點是祖父結點的右子結點
該情景對應情景4.2,只是方向反轉,不作過多說明了,直接看圖。

插入情景4.3.1:插入結點是其父結點的右子結點
處理:

  • 將P設爲黑色
  • 將PP設爲紅色
  • 對PP進行左旋
 
圖13 插入情景4.3.1

插入情景4.3.2:插入結點是其父結點的右子結點
處理:

  • 對P進行右旋
  • 把P設置爲插入結點,獲得情景4.3.1
  • 進行情景4.3.1的處理
 
圖14 插入情景4.3.2

好了,講完插入的全部情景了。可能又同窗會想:上面的情景舉例的都是第一次插入而不包含自底向上處理的狀況,那麼上面所說的情景都適合自底向上的狀況嗎?答案是確定的。理由很簡單,但每棵子樹都能自平衡,那麼整棵樹最終老是平衡的。好吧,在出個習題,請你們拿出筆和紙畫下試試(請務必動手畫下,加深印象):

習題1:請畫出圖15的插入自平衡處理過程。(答案見文末)

圖15 習題1

紅黑樹刪除

紅黑樹插入已經夠複雜了,但刪除更復雜,也是紅黑樹最複雜的操做了。但穩住,勝利的曙光就在前面了!

紅黑樹的刪除操做也包括兩部分工做:一查找目標結點;而刪除後自平衡。查找目標結點顯然能夠複用查找操做,當不存在目標結點時,忽略本次操做;當存在目標結點時,刪除後就得作自平衡處理了。刪除告終點後咱們還須要找結點來替代刪除結點的位置,否則子樹跟父輩結點斷開了,除非刪除結點恰好沒子結點,那麼就不須要替代。

二叉樹刪除結點找替代結點有3種情情景:

  • 情景1:若刪除結點無子結點,直接刪除
  • 情景2:若刪除結點只有一個子結點,用子結點替換刪除結點
  • 情景3:若刪除結點有兩個子結點,用後繼結點(大於刪除結點的最小結點)替換刪除結點

補充說明下,情景3的後繼結點是大於刪除結點的最小結點,也是刪除結點的右子樹種最左結點。那麼能夠拿前繼結點(刪除結點的左子樹最左結點)替代嗎?能夠的。但習慣上大多都是拿後繼結點來替代,後文的講解也是用後繼結點來替代。另外告訴你們一種找前繼和後繼結點的直觀的方法(不知爲什麼沒人提過,你們都知道?):把二叉樹全部結點投射在X軸上,全部結點都是從左到右排好序的,全部目標結點的先後結點就是對應前繼和後繼結點。如圖16所示。

 
圖16 二叉樹投射x軸後有序

接下來,講一個重要的思路:刪除結點被替代後,在不考慮結點的鍵值的狀況下,對於樹來講,能夠認爲刪除的是替代結點!話很蒼白,咱們看圖17。在不看鍵值對的狀況下,圖17的紅黑樹最終結果是刪除了Q所在位置的結點!這種思路很是重要,大大簡化了後文講解紅黑樹刪除的情景!

 
圖17 刪除結點換位思路

基於此,上面所說的3種二叉樹的刪除情景能夠相互轉換而且最終都是轉換爲情景1!

  • 情景2:刪除結點用其惟一的子結點替換,子結點替換爲刪除結點後,能夠認爲刪除的是子結點,若子結點又有兩個子結點,那麼至關於轉換爲情景3,一直自頂向下轉換,老是能轉換爲情景1。(對於紅黑樹來講,根據性質5.1,只存在一個子結點的結點確定在樹末了)
  • 情景3:刪除結點用後繼結點(確定不存在左結點),若是後繼結點有右子結點,那麼至關於轉換爲情景2,不然轉爲爲情景1。

二叉樹刪除結點情景關係圖如圖18所示。

 
圖18 二叉樹刪除情景轉換

綜上所述,刪除操做刪除的結點能夠看做刪除替代結點,而替代結點最後老是在樹末。有了這結論,咱們討論的刪除紅黑樹的情景就少了不少,由於咱們只考慮刪除樹末結點的情景了。

一樣的,咱們也是先來整體看下刪除操做的全部情景,如圖19所示。

圖19 紅黑樹刪除情景

哈哈,是的,即便簡化了仍是有9種情景!但跟插入操做同樣,存在左右對稱的情景,只是方向變了,沒有本質區別。一樣的,咱們仍是來約定下,如圖20所示。

 
圖20 刪除操做結點的叫法約定

圖20的字母並不表明結點Key的大小。R表示替代結點,P表示替代結點的父結點,S表示替代結點的兄弟結點,SL表示兄弟結點的左子結點,SR表示兄弟結點的右子結點。灰色結點表示它能夠是紅色也能夠是黑色。

值得特別提醒的是,R是即將被替換到刪除結點的位置的替代結點,在刪除前,它還在原來所在位置參與樹的子平衡,平衡後再替換到刪除結點的位置,纔算刪除完成。

萬事具有,咱們進入最後的也是最難的講解。

刪除情景1:替換結點是紅色結點

咱們把替換結點換到了刪除結點的位置時,因爲替換結點時紅色,刪除也了不會影響紅黑樹的平衡,只要把替換結點的顏色設爲刪除的結點的顏色便可從新平衡。

處理:顏色變爲刪除結點的顏色

刪除情景2:替換結點是黑結點

當替換結點是黑色時,咱們就不得不進行自平衡處理了。咱們必須還得考慮替換結點是其父結點的左子結點仍是右子結點,來作不一樣的旋轉操做,使樹從新平衡。

刪除情景2.1:替換結點是其父結點的左子結點
刪除情景2.1.1:替換結點的兄弟結點是紅結點
若兄弟結點是紅結點,那麼根據性質4,兄弟結點的父結點和子結點確定爲黑色,不會有其餘子情景,咱們按圖21處理,獲得刪除情景2.1.2.3(後續講解,這裏先記住,此時R仍然是替代結點,它的新的兄弟結點SL和兄弟結點的子結點都是黑色)。

處理:

  • 將S設爲黑色
  • 將P設爲紅色
  • 對P進行左旋,獲得情景2.1.2.3
  • 進行情景2.1.2.3的處理
 
圖21 刪除情景2.1.1

刪除情景2.1.2:替換結點的兄弟結點是黑結點
當兄弟結點爲黑時,其父結點和子結點的具體顏色也沒法肯定(若是也不考慮自底向上的狀況,子結點非紅即爲葉子結點Nil,Nil結點爲黑結點),此時又得考慮多種子情景。

刪除情景2.1.2.1:替換結點的兄弟結點的右子結點是紅結點,左子結點任意顏色
即將刪除的左子樹的一個黑色結點,顯然左子樹的黑色結點少1了,然而右子樹又又紅色結點,那麼咱們直接向右子樹「借」個紅結點來補充黑結點就好啦,此時確定須要用旋轉處理了。如圖22所示。

處理:

  • 將S的顏色設爲P的顏色
  • 將P設爲黑色
  • 將SR設爲黑色
  • 對P進行左旋
 
圖22 刪除情景2.1.2.1

平衡後的圖怎麼不知足紅黑樹的性質?前文提醒過,R是即將替換的,它還參與樹的自平衡,平衡後再替換到刪除結點的位置,因此R最終能夠看做是刪除的。另外圖2.1.2.1是考慮到第一次替換和自底向上處理的狀況,若是隻考慮第一次替換的狀況,根據紅黑樹性質,SL確定是紅色或爲Nil,因此最終結果樹是平衡的。若是是自底向上處理的狀況,一樣,每棵子樹都保持平衡狀態,最終整棵樹確定是平衡的。後續的情景同理,不作過多說明了。

刪除情景2.1.2.2:替換結點的兄弟結點的右子結點爲黑結點,左子結點爲紅結點
兄弟結點所在的子樹有紅結點,咱們老是能夠向兄弟子樹借個紅結點過來,顯然該情景能夠轉換爲情景2.1.2.1。圖如23所示。

處理:

  • 將S設爲紅色
  • 將SL設爲黑色
  • 對S進行右旋,獲得情景2.1.2.1
  • 進行情景2.1.2.1的處理
 
圖23 刪除情景2.1.2.2

刪除情景2.1.2.3:替換結點的兄弟結點的子結點都爲黑結點
好了,這次兄弟子樹都沒紅結點「借」了,兄弟幫忙不了,找父母唄,這種情景咱們把兄弟結點設爲紅色,再把父結點看成替代結點,自底向上處理,去找父結點的兄弟結點去「借」。但爲何須要把兄弟結點設爲紅色呢?顯然是爲了在P所在的子樹中保證平衡(R即將刪除,少了一個黑色結點,子樹也須要少一個),後續的平衡工做交給父輩們考慮了,仍是那句,當每棵子樹都保持平衡時,最終整棵老是平衡的。

處理:

  • 將S設爲紅色
  • 把P做爲新的替換結點
  • 從新進行刪除結點情景處理
 
圖24 情景2.1.2.3

刪除情景2.2:替換結點是其父結點的右子結點
好啦,右邊的操做也是方向相反,不作過多說明了,相信理解了刪除情景2.1後,確定能夠理解2.2。

刪除情景2.2.1:替換結點的兄弟結點是紅結點
處理:

  • 將S設爲黑色
  • 將P設爲紅色
  • 對P進行右旋,獲得情景2.2.2.3
  • 進行情景2.2.2.3的處理
 
圖25 刪除情景2.2.1

刪除情景2.2.2:替換結點的兄弟結點是黑結點
刪除情景2.2.2.1:替換結點的兄弟結點的左子結點是紅結點,右子結點任意顏色
處理:

  • 將S的顏色設爲P的顏色
  • 將P設爲黑色
  • 將SL設爲黑色
  • 對P進行右旋
 
圖26 刪除情景2.2.2.1

刪除情景2.2.2.2:替換結點的兄弟結點的左子結點爲黑結點,右子結點爲紅結點
處理:

  • 將S設爲紅色
  • 將SR設爲黑色
  • 對S進行左旋,獲得情景2.2.2.1
  • 進行情景2.2.2.1的處理
 
圖27 刪除情景2.2.2.2

刪除情景2.2.2.3:替換結點的兄弟結點的子結點都爲黑結點
處理:

  • 將S設爲紅色
  • 把P做爲新的替換結點
  • 從新進行刪除結點情景處理
 
圖28 刪除情景2.2.2.3

綜上,紅黑樹刪除後自平衡的處理能夠總結爲:

  1. 本身能搞定的自消化(情景1)
  2. 本身不能搞定的叫兄弟幫忙(除了情景一、情景2.1.2.3和情景2.2.2.3)
  3. 兄弟都幫忙不了的,經過父母,找遠方親戚(情景2.1.2.3和情景2.2.2.3)

哈哈,是否是跟現實中很像,當咱們有困難時,首先先本身解決,本身無力了總兄弟姐妹幫忙,若是連兄弟姐妹都幫不上,再去找遠方的親戚了。這裏記憶應該會好記點~

最後再作個習題加深理解(請不熟悉的同窗務必動手畫下):

***習題2:請畫出圖29的刪除自平衡處理過程。

 
習題2


寫在後面

耗時良久,終於寫完了~本身加深了紅黑樹的理解的同時,也但願能幫助你們。若是你以前沒學習過紅黑樹,看完這篇文章後可能還存在不少疑問,若是有疑問能夠在評論區寫出來,我會盡本身所能解答。另外給你們推薦一個支持紅黑樹在線生成的網站,來作各類情景梳理頗有幫助:在線生成紅黑樹。(刪除操做那個把替代結點看做刪除結點思路就是我本身在用這個網站時本身頓悟的,我以爲這樣講解更容易理解。)

少了代碼是否是以爲有點空虛?哈哈,後續我會寫關於Java和HashMap和TreeMap的文章,裏面都有紅黑樹相關的知識。相信看了這篇文章後,再去看Java和HashMap和TreeMap的源碼絕對沒難度!

最後來看下思考題和習題的答案吧。


思考題和習題答案

思考題1:黑結點能夠同時包含一個紅子結點和一個黑子結點嗎?
答:能夠。以下圖的F結點:

 
 

 

習題1:請畫出圖15的插入自平衡處理過程。
答:

 
 

 

習題2:請畫出圖29的刪除自平衡處理過程。
答:

 
原文連接:https://www.jianshu.com/p/e136ec79235c
相關文章
相關標籤/搜索