二叉查找樹因爲在頻繁的動態更新過程當中,可能會出現樹的高度遠大於 log2n
的狀況,因此就會致使各個操做效率降低,最壞的狀況下就會退化爲鏈表,變爲O(n).很明顯,想要解決這個問題,有效的一種辦法就是使得樹的高度不要差不少,也就是平衡它.web
最早發明的平衡二叉查找樹是AVL樹,(它嚴格符合平衡二叉查找樹的定義,即任何節點的左右子樹高度相差不超過 1,是一種高度平衡的二叉查找樹。
)可是在工程中,咱們常常聽到的一般是紅黑樹
,而不是AVL樹.那麼爲何工程中都喜歡用紅黑樹,而不是其餘平衡二叉查找樹呢?
算法
其實在這裏,咱們應該能有一些想法了.既然他嚴格按照規定執行,每次的插入,刪除,都就會引起樹的調整.調整的多了,天然會影響樹的效率.那麼紅黑樹又是怎樣解決這個問題的吶?svg
紅黑樹的英文是「Red-Black Tree」,簡稱 R-B Tree。它是一種不嚴格的平衡二叉查找樹.
顧名思義,紅黑樹中的節點,一類被標記爲黑色,一類被標記爲紅色。除此以外,一棵紅黑樹還須要知足這樣四個要求:性能
這裏的第二點要求「葉子節點都是黑色的空節點」,主要是爲了簡化紅黑樹的代碼實現而設置的.如今,咱們暫時無論這一點.動畫
下面是兩個紅黑樹的圖例,你能夠看下。
.net
首先,咱們知道二叉查找樹不少操做的性能都跟樹的高度成正比。一棵極其平衡的二叉樹(滿二叉樹或徹底二叉樹)的高度大約是 log2n,因此若是要證實紅黑樹是近似平衡的,咱們只須要分析,紅黑樹的高度是否比較穩定地趨近 log2n 就行了。設計
紅色節點刪除以後,有些節點就沒有父節點了,它們會直接拿這些節點的祖父節點(父節點的父節點)做爲父節點。因此,以前的二叉樹就變成了四叉樹。
3d
這時咱們能夠將有些節點拿出來組成一個徹底二叉樹,而徹底二叉樹的高度是 log2n
,很明顯,咱們的四叉樹根本不會高於 log2n
code
從上面我畫的紅黑樹的例子和定義看,在紅黑樹中,紅色節點不能相鄰,也就是說,有一個紅色節點就要至少有一個黑色節點,將它跟其餘紅色節點隔開.紅黑樹中包含最多黑色節點的路徑不會超過 log2n,因此加入紅色節點以後,最長路徑不會超過 2log2n,也就是說,紅黑樹的高度近似 2log2n。xml
因此,紅黑樹的高度只比高度平衡的 AVL 樹的高度(log2n)僅僅大了一倍,在性能上,降低得並很少。這樣推導出來的結果不夠精確,實際上紅黑樹的性能更好。
OK,紅黑樹的原理咱們已經知道了,那麼咱們如今就來了解一下它的實現思想
在極客時間中老師用了魔方的例子來說解其思想,我以爲很恰當.這裏直接拿來用
不知道你有沒有玩過魔方?其實魔方的復原解法是有固定算法的:遇到哪幾面是什麼樣子,對應就怎麼轉幾下。你只要跟着這個復原步驟,就確定能將魔方復原。
實際上,紅黑樹的平衡過程跟魔方復原很是神似,大體過程就是:遇到什麼樣的節點排布,咱們就對應怎麼去調整。只要按照這些固定的調整規則來操做,就能將一個非平衡的紅黑樹調整成平衡的。
其實想一想AVL樹,不就是這樣嗎?在想一想計算機,不就是不斷"重複"的去作一些事情的嗎?
和AVL樹同樣,在進行節點的插入和刪除時,就會破壞紅黑樹的一些規則
.在紅黑樹中主要破壞的是如下兩點:
很顯然,咱們須要也僅僅須要處理的就是如何把被破壞了的這兩點規則還原回去.在還原以前,咱們須要瞭解兩個很重要的操做:左旋與右旋(圍繞某個節點的右旋),
如圖,其中 a,b,r 表示子樹,能夠爲空。
感受有個動畫的效果是最好的了,哈哈哈~~不過也能夠本身在腦中想象了啦
紅黑樹規定,插入的節點必須是紅色的。並且,二叉查找樹中新插入的節點都是放在葉子節點上
。因此,關於插入操做的平衡調整,有這樣兩種特殊狀況,可是也都很是好處理。
1.若是插入節點的父節點是黑色的,那咱們什麼都不用作,它仍然知足紅黑樹的定義。
2. 若是插入的節點是根節點,那咱們直接改變它的顏色,把它變成黑色就能夠了。
其餘:都會違背紅黑樹的定義,因而咱們就須要進行調整,調整的過程包含兩種基礎的操做:左右旋轉和改變顏色。
其餘狀況主要有三種,若是要實現,能夠對應各個狀況各個擊破,紅黑樹的平衡調整過程是一個迭代的過程。就想魔方同樣!!!對應規則調整就好了.
刪除操做的平衡調整分爲兩步,第一步是針對刪除節點初步調整。初步調整隻是保證整棵紅黑樹在一個節點刪除以後,仍然知足最後一條定義的要求,也就是說,每一個節點,從該節點到達其可達葉子節點的全部路徑,都包含相同數目的黑色節點;第二步是針對關注節點進行二次調整,讓它知足紅黑樹的第三條定義,即不存在相鄰的兩個紅色節點。
紅黑樹的定義中「只包含紅色節點和黑色節點」,通過初步調整以後,爲了保證知足紅黑樹定義的最後一條要求,有些節點會被標記成兩種顏色
,「紅 - 黑」或者「黑 - 黑」。若是一個節點被標記爲了「黑 - 黑」,那在計算黑色節點個數的時候,要算成兩個黑色節點。
這裏具體有:三種狀況
通過初步調整以後,關注節點變成了「紅 - 黑」或者「黑 - 黑」節點。針對這個關注節點,咱們再分四種狀況來進行二次調整。二次調整是爲了讓紅黑樹中不存在相鄰的紅色節點。
這裏具體有:四種狀況
如何理解紅黑樹定義的"任何相鄰的節點都不能同時爲紅色"?
若是一個結點是紅色的,則它的兩個孩子都是黑色的.也就是說只要用線連起來的都不能同時是紅色
如何理解紅黑樹定義的"每一個節點,從該節點到達其可達葉子節點的全部路徑,都包含相同數目的黑色節點; "?
nullptr .
爲何插入的節點恰恰是紅色呢?
將插入的結點着色爲紅色,不會違背 「性質 4」。而少違背一條性質,就意味着咱們須要處理的狀況越少。
只要知足這一條要求,那在任什麼時候刻,紅黑樹的平衡操做均可以歸結爲上面的那幾種狀況。並且是簡潔的.
一張圖給你,立馬明白:
時間複雜度其實都跟樹的高度成正比,也就是 O(height)。既然這樣,如今問題就轉變成另一個了,也就是,如何求一棵包含 n 個節點的徹底二叉樹的高度?
樹的高度就等於最大層數減一,爲了方便計算,咱們轉換成層來表示。從圖中能夠看出,包含 n 個節點的徹底二叉樹中,第一層包含 1 個節點,第二層包含 2 個節點,第三層包含 4 個節點,依次類推,下面一層節點個數是上一層的 2 倍,第 K 層包含的節點個數就是 2^(K-1)。
不過,對於徹底二叉樹來講,最後一層的節點個數有點兒不遵照上面的規律了。它包含的節點個數在 1 個到 2^(L-1) 個之間(咱們假設最大層數是 L)。若是咱們把每一層的節點個數加起來就是總的節點個數 n。也就是說,若是節點的個數是 n,那麼 n 知足這樣一個關係:
n >= 1+2+4+8+...+2^(L-2)+1 n <= 1+2+4+8+...+2^(L-2)+2^(L-1)
藉助等比數列的求和公式,咱們能夠計算出,L 的範圍是 [log2(n+1), log2n +1]。徹底二叉樹的層數小於等於 log2n +1,也就是說,徹底二叉樹的高度小於等於 log2n。