1. 程式人生 > >紅黑樹簡介(Introduction to Red-black tree)

紅黑樹簡介(Introduction to Red-black tree)

紅黑樹簡介(Introduction to Red-black tree)

作者:Bluemapleman([email protected])

麻煩不吝star和fork本博文對應的github上的技術部落格專案吧!謝謝你們的支援!

知識無價,寫作辛苦,歡迎轉載,但請註明出處,謝謝!


文章目錄


前言:紅黑樹以操作複雜,但效能優異著稱(增刪查節點最壞情況的時間複雜度都是O(lgn))。Java的TreeSet和TreeMap都是基於紅黑樹實現的。紅黑樹屬於BST(二叉查詢樹)的一種,但它相比BST多了平衡的(balanced)特徵,平衡的含義是:“最壞情況下的樹高也只是O(lgn)”。而平衡的特性,也就是紅黑樹能保證優異效能的根本所在。

定義

(可以先嚐試瞭解2-3-4樹的概念,2-3-4樹也是一種自平衡的樹,可以保證在O(lgn)內完成增刪查操作,但是由於實現相對較為困難,所以在要求實現高效能的樹時往往用效能相似的紅黑樹來替代。2-3-4樹資料

而紅黑樹具備以下五個特徵的二叉搜尋樹:

  • 每個節點要麼是紅色,要麼是黑色
  • 根是黑色
  • 每個葉子節點(NIL)都是黑色
  • 不能父子節點同時為紅色
  • 任意一條從根到葉子的路徑上都有相同數目的黑色結點

pic1

淺色為紅色,深色為黑色,圖來自Introduction To Algorithm: Third Edition, Thomas et al. Page 309 [1]

NIL表示預設存在的黑色葉子節點,後面畫圖和分析時都可以忽略這些NIL結點。它們存在的意義主要在於滿足紅黑樹的葉子節點顏色的要求。

紅黑樹的高度h有這麼一條可以證明的特性:有n個keys的紅黑樹的高度h,小於等於2lg(n+1)。(證明可參看[1])

另外,就平衡這個特性來說,我們還可以將紅黑樹與AVL樹做一個對比:

紅黑樹與AVL樹的比較:(來自AVL樹與紅黑樹(R-B樹)的區別與聯絡

AVL是嚴格的平衡樹,因此在增加或者刪除節點的時候,根據不同情況,旋轉的次數比紅黑樹要多;

紅黑樹是用非嚴格的平衡來換取增刪節點時候旋轉次數的降低開銷;

所以簡單說,如果你的應用中,搜尋的次數遠遠大於插入和刪除,那麼選擇AVL樹,

如果搜尋,插入刪除次數幾乎差不多,應選擇紅黑樹。即,有時僅為了排序(建立-遍歷-刪除),不查詢或查詢次數很少,R-B樹合算一些。
--------------------- 
作者:碼農的小夢想 
來源:CSDN 
原文:https://blog.csdn.net/zhangkunrun/article/details/38336543 

查詢操作分析

紅黑樹的查詢操作與BST樹完全相同,用查詢的值與根節點的key做比較,找到即返回,沒找到就遞迴地在根節點的左子樹或右子樹裡繼續找即可。時間複雜度顯然=樹高=O(lgn)。

插入操作分析

紅黑樹的插入和標準的BST樹的插入類似:我們首先通過不斷地比較,找到新節點應當插入的葉子節點的位置,如果發現插入結點的key已存在,那麼就不需要後續操作了。如果沒有,我們就將新節點接在相應的位置上,並將新節點染成紅色

這樣做的潛在問題是:如果插入新節點位置的父親節點也是個紅色結點,那麼這樣做就會導致父子節點都是紅色,這是不符合紅黑樹規定的。所以,紅黑樹完整的插入操作還包含一步:解決可能存在的“紅-紅”問題。

為了更好地解決“紅紅問題”,我們把新節點插入後,可能出現的所有情況分成以下四種:

  • case 0:父親節點為黑色,搞定收工!
  • case 1:父親和叔伯結點均為紅色,則將爺爺結點(一定為黑色)染成紅色,父親和叔伯結點均染成黑色,換言之,做一個顏色翻轉!之後,我們再看爺爺結點與爺爺結點的父親節點是否有“紅紅問題”存在;
  • case 2:父親為紅色,叔伯不存在或叔伯為黑色,則若父親和爺爺結點此時處在插入結點的不同側,旋轉一次;
  • case 3:父親為紅色,叔伯不存在或叔伯為黑色,則若父親和爺爺結點此時處在插入結點的相同側,旋轉一次;

我們根據圖來看一下這幾種情況,並詳細講述一下相關的具體操作:

pic2

(1) 我們用變數z來指向我們新插入的節點,或者指向我們後續要處理的結點。可以看上圖中,我們新插入的結點是4,按照BST的方式,判斷它應該成為結點5的左子節點,於是我們插入4,並將4染成紅色(a)。

(2) 然後,我們發現4的父親4和叔伯8都是紅色,符合case1,於是我們將爺爺結點和父親叔伯結點的顏色互換,之後變數z指向爺爺結點7,因為我們已經保證爺爺結點以下的結點沒有“紅紅問題”了(b)。

(3) 然而此時,我們又發現z指向的結點7的父親2也是紅色,然而叔伯14是黑色,並且父親2和爺爺11不在同一側,故符合case2,此時,我們先將z指向z本來的父親2,然後我們針對現在的z做一次樹的旋轉(此處是左旋)(c)。

(4) 左旋完後,我們再看z,返現此時z的父親7為紅色,叔伯14為黑色,而父親7和爺爺11在同一側,故滿足case3,於是我們針對z的爺爺結點11做一個旋轉(此處是右旋)(d)。

(5) 最後,我們會發現z的父親7跟著旋轉到根節點,但是父親本身是紅色的,而紅黑樹要求根是黑色的,所以我們把父親結點7染成黑色。

現在,這個紅黑樹就在插入新節點後依然滿足所有紅黑樹的要求。這樣,我們就徹底完成了紅黑樹的插入。

紅黑樹插入的虛擬碼:(先插入,後修復)

pic3-1
pic3-2

這裡需要注意的一點是:從修復部分的虛擬碼也可以看出,當我們遇到case2並完成處理後,我們一定會緊接著遇到case3,而處理完case3後,我們一定就已經基本解決了所有的“紅紅問題”(可能還需要把根節點染黑)。而只有case1的情況是可能出現自我迴圈的,即需要多次翻轉父親叔伯與爺爺的顏色,而case1若不迴圈,我們還有可能再碰到case 0,2,3。瞭解這四個case之間的一個轉換關係可以幫助我們分析插入操作的時間複雜度。

pic4

課堂上老師的板書。

  • 時間複雜度

由於具有n個節點的紅黑樹的高度是O(lgn),除開修復部分的插入操作顯然只花費O(lgn)的時間,而在修復操作中,while loop修復“紅紅問題”的過程中,只有case1可能使while loop繼續迴圈(case2會轉到case3,case3代表結束),而每次case1迴圈會使得指標z指向指向更高兩層的節點(仔細理解上面的修復過程),因此while loop迴圈的次數也最多隻有O(lgn)。另外,case2和case3中的旋轉操作也都是O(1)的時間複雜度。因此,紅黑樹插入操作的總時間複雜度為O(lgn)。

刪除操作

開始的步驟類似BST中的刪除操作(Hibbard Deletion):首先找到要刪除的結點z:

  • 若該結點只有一個子節點,則直接用該子節點替換在待刪除結點z的位置就好;

  • 若待刪除結點有兩個子節點(左子節點為l):

    • 若右子節點y作為根節點的子樹沒有左子樹/左子節點,則直接用y替換在z的位置,然後l作為z的左子節點即可;
    • 則若右子節點r作為根節點還有左子樹,則取該左子樹中key最小的那個結點作為y,那麼我們需要將r作為y的右子樹,l作為y的左子樹,用y替換在z的位置即可。

pic5

但是紅黑樹還需要除以上正常的BST刪除步驟之外進行一些其它的調整,這些調整比較複雜,本文就不具體分析了。(我們老師也只講到這裡,但是給大家推薦一篇講刪除的博文

但記住紅黑樹的刪除操作時間複雜度也是O(lgn)即可。

參考文獻

[1] Introduction to Algorithms: Third Edition, Thomas et al.