十三、红黑树
實際工程中,很多用到平衡二叉查找樹的地方都是紅黑樹。
==》為何是紅黑樹?而不是其他平衡二叉查找樹?
- 其他二叉樹不適用于單次操作時間非常敏感的場景。
- AVL樹對于頻繁的插入、刪除操作的數(shù)據(jù)集合代價有些高==》紅黑樹維護成本比AVL要低
一、平衡二叉查找樹
平衡二叉樹:二叉樹中任意一個節(jié)點的左右子樹的高度相差不能大于1。
完全二叉樹、滿二叉樹都是平衡二叉樹;但非完全二叉樹也可能是平衡二叉樹
AVL樹——最早被發(fā)明的平衡二叉查找樹
“平衡二叉樹”思想:解決普通二叉查找樹在頻繁的插入、刪除等動態(tài)更新的情況下,出現(xiàn)時間復(fù)雜度退化的問題。
==》整棵樹左右更對稱些,不要出現(xiàn)左子樹很高、右子樹很矮的情況,相應(yīng)的插入、刪除、查找等操作的效率高一些。
==》設(shè)計新的平衡二叉樹,只要樹的高度不比log2n大很多,就可以說其實一個合格的平衡二叉樹。
1、紅黑樹(Red-Black Tree,簡稱R-B Tree)
- 紅黑樹是一種不嚴(yán)格的平衡二叉查找樹
(1)紅黑樹:
- 根節(jié)點是黑色的;
- 每個葉子節(jié)點都是黑色的空節(jié)點(NIL),也就是說,葉子節(jié)點不存儲數(shù)據(jù);
- 任何相鄰節(jié)點都不能同時為紅色,也就是說,紅色節(jié)點被黑色節(jié)點隔開的;
- 每個節(jié)點,從該節(jié)點到其可達葉子節(jié)點的所有路徑,都包含相同數(shù)目的黑色節(jié)點。
(2)高度分析
-
將紅色節(jié)點從紅黑樹中去掉,只留黑色節(jié)點
==》有些節(jié)點沒有父節(jié)點,它們會將這些節(jié)點的祖父節(jié)點作為父節(jié)點
==》會出現(xiàn)多叉樹(eg: 四叉樹的情況)
==》僅包含黑色節(jié)點的多叉樹的高度,比包含相同節(jié)點個數(shù)的完全二叉樹的高度要小。
==》去掉紅色節(jié)點的“黑樹”的高度不超過log2n。
-
將紅色節(jié)點添加回去——紅色節(jié)點不能相鄰,也就是說有一個紅色節(jié)點就要至少有一個黑色節(jié)點,將其跟其他紅色節(jié)點隔開
==》高度不會超過2log2n,也就是說,紅黑樹的高度近似2log2n。
(3)小結(jié)
紅黑樹的高度只比高度平衡的AVL樹的高度(log2n)僅僅大了一倍,在性能上,下降得并不多。
二、平衡調(diào)整
1、左旋(rotate left)和右旋(rotate right)
紅黑樹的插入、刪除操作會破壞紅黑樹的定義,也就是說,會破壞紅黑樹的平衡性。
左旋:圍繞某個節(jié)點的左旋。
右旋:圍繞某個節(jié)點的右旋。
2、插入操作的平衡調(diào)整
紅黑樹規(guī)定:插入的節(jié)點必須是紅色的,而且新插入的節(jié)點必須放在葉子節(jié)點上。
(1)兩種特殊情況:
- 若插入節(jié)點的父親節(jié)點是黑色的,則不需要做什么。因為:它仍滿足紅黑樹的定義
- 若插入的節(jié)點是根節(jié)點,則直接改變其顏色,將其變?yōu)楹谏?/li>
- ==》其他情況,均違背紅黑樹的定義,則需要進行調(diào)整——左右旋轉(zhuǎn)、改變顏色
(2)平衡調(diào)整過程——迭代過程
關(guān)注關(guān)節(jié):正在處理的節(jié)點。它會隨著不停地迭代處理,而不斷發(fā)生變化。
==》最開始的關(guān)注節(jié)點就是新插入的接待呢
新節(jié)點插入后,若平衡被打破,則一般會出現(xiàn)下面三種情況:
(這里,將父節(jié)點的兄弟節(jié)點稱為叔叔節(jié)點,父節(jié)點的父節(jié)點稱為祖父節(jié)點)
case1 : 若關(guān)注節(jié)點是 a,它的叔叔節(jié)點d是紅色,則
- 將關(guān)注節(jié)點 a 的父節(jié)點 b 、叔叔節(jié)點 d 的顏色均設(shè)置為黑色;
- 將關(guān)注節(jié)點 a 的祖父節(jié)點 c 的顏色設(shè)置為紅色;
- 關(guān)注節(jié)點變成 a 的祖父節(jié)點 c ;
- 跳轉(zhuǎn)case2 或者 case3
case2:若關(guān)注節(jié)點是a,它叔叔節(jié)點d是黑色,關(guān)注節(jié)點a是其父節(jié)點b的右子節(jié)點,則:
- 關(guān)注節(jié)點變?yōu)楣?jié)點 a 的父節(jié)點 b;
- 圍繞新的關(guān)注節(jié)點 b 左旋;
- 跳轉(zhuǎn) case3
case3:若關(guān)注節(jié)點是 a,它叔叔節(jié)點 d 是黑色,關(guān)注節(jié)點 a 是其父節(jié)點b的左子結(jié)點,則
- 圍繞關(guān)注節(jié)點 a 的祖父節(jié)點 c 右旋;
- 將關(guān)注節(jié)點 a 的父節(jié)點 b、兄弟節(jié)點 c 的顏色互換;
- 調(diào)整結(jié)束
3、刪除操作的平衡調(diào)整
思路:根據(jù)關(guān)注節(jié)點與周圍節(jié)點的排布特點,按照一定的規(guī)則去調(diào)整來達到平衡
刪除操作的平衡調(diào)整方法:
- 第一步,針對刪除節(jié)點初步調(diào)整。
- 使其的每個節(jié)點,從該節(jié)點到達其可達葉子節(jié)點的所有路徑,都包含相同數(shù)目的黑色節(jié)點(紅黑樹定義中最后一條)
- 第二步,針對關(guān)注節(jié)點進行二次調(diào)整。
- ==》不存在相鄰的兩個紅色節(jié)點(紅黑樹定義中第三條)
(1)針對刪除節(jié)點初步調(diào)整
注1:有些節(jié)點會被標(biāo)記成兩種顏色,“紅 - 黑” 或 “黑 - 黑”。若標(biāo)記為“黑 - 黑”,則在計算黑色節(jié)點個數(shù)時,要算成兩個黑色節(jié)點。
注2:畫圖時:若一個節(jié)點既可以是紅色,又可以是黑色==》用一半紅色一半黑色表示;若一個節(jié)點是“紅 - 黑”或“黑 - 黑”==》在左上角用小黑點表示額外的黑色。
case1:若要刪除的節(jié)點是 a,它只有一個子節(jié)點 b,則:
- 刪除節(jié)點a,并把節(jié)點 b 替換到節(jié)點 a 的位置;(同普通的二叉樹刪除操作一樣)
- 節(jié)點 a 只能是黑色,節(jié)點 b 也只能是紅色,其他情況均不符合紅黑樹定義。這種情況下,將節(jié)點 b 改為黑色。。
- 調(diào)整結(jié)束,不需要進行二次調(diào)整。
case2:若要刪除的節(jié)點 a 有兩個非空子節(jié)點,并且它的后繼節(jié)點就是節(jié)點 a 的右子節(jié)點 c,則
- 若節(jié)點 a 的后繼節(jié)點就是右子節(jié)點 c,那右子節(jié)點 c 肯定沒有左子樹。則把節(jié)點 a 刪除,并且將節(jié)點 c 替換到節(jié)點 a 的位置。(該部分與普通的二叉查找樹的刪除操作相同)
- 然后將節(jié)點 c 的顏色設(shè)置為與節(jié)點 a 相同的顏色;
- 若節(jié)點 c 是黑色,為了不違反紅黑樹最后一條定義,將節(jié)點 c 的右子節(jié)點 d 多加一個黑色,此時節(jié)點 d 就成了“紅 - 黑”或“黑 - 黑”;
- 此時,關(guān)注節(jié)點變成節(jié)點 d,第二步的調(diào)整就針對關(guān)注節(jié)點進行操作
case3:若要刪除的是節(jié)點 a,有兩個非空子節(jié)點,并且節(jié)點 a 的后繼節(jié)點不是右子節(jié)點,則:
- 找到后繼節(jié)點 d,并將其刪除,刪除后繼節(jié)點 d 的過程參考 case 1;
- 將節(jié)點 a 替換成后繼結(jié)點 d;
- 把節(jié)點 d 的顏色設(shè)置為與節(jié)點 a 相同的顏色;
- 若節(jié)點 d 是黑色,將節(jié)點 d 的右子節(jié)點 c 多加一個黑色,則節(jié)點 c 變成“紅 - 黑”或“黑 - 黑”(紅黑樹定義最后一條)
- 關(guān)注節(jié)點變成節(jié)點 c,第二步調(diào)整操作針對關(guān)注點進行調(diào)整。
(2)針對關(guān)注節(jié)點二次調(diào)整
經(jīng)過初步調(diào)整,關(guān)注節(jié)點變成了“紅 - 黑”或“黑 - 黑”節(jié)點。
二次調(diào)整的目的:紅黑樹中不存在相鄰的紅色節(jié)點
==》四種情況:
case1:若關(guān)注節(jié)點是 a,其兄弟節(jié)點 c 是紅色,則:
- 圍繞關(guān)注節(jié)點 a 的父節(jié)點 b 左旋;
- 關(guān)注節(jié)點 a 的父節(jié)點 b 和 祖父節(jié)點 c 交換顏色;
- 關(guān)注節(jié)點不變;
- 繼續(xù)從四種情況中選擇合適的規(guī)則來調(diào)整。
case2:若關(guān)注節(jié)點是 a,其兄弟節(jié)點 c 是黑色,并且節(jié)點 c 的 左右子節(jié)點 d、e都是黑色,則:
- 將關(guān)注節(jié)點 a 的兄弟節(jié)點 c 的顏色設(shè)置為 紅色;
- 從關(guān)注節(jié)點 a 中去掉一個黑色==》節(jié)點 a 為單純的紅色或黑色;
- 給關(guān)注節(jié)點 a 的父節(jié)點 b 添加一個黑色==》節(jié)點 b 變成“紅 - 黑”或者“黑 - 黑”;
- 關(guān)注節(jié)點從 a 變成其父節(jié)點 b;
- 繼續(xù)從四種情況中選擇符合的規(guī)則來調(diào)整。
case3:關(guān)注節(jié)點是 a,其兄弟節(jié)點 c 是黑色,c 的左子結(jié)點 d 是紅色,c 的右子節(jié)點 e 是黑色,則:
- 圍繞關(guān)注節(jié)點 a 的兄弟節(jié)點 c 左旋;
- 節(jié)點 c 和節(jié)點 d 交換顏色;
- 關(guān)注節(jié)點不變;
- 跳轉(zhuǎn)case4,繼續(xù)調(diào)整。
case4:若關(guān)注節(jié)點 a 的兄弟節(jié)點 c 是黑色,且 c 的右子節(jié)點是紅色,則:
- 圍繞關(guān)注節(jié)點 a 的父節(jié)點 b 左旋;
- 將關(guān)注節(jié)點 a 的兄弟節(jié)點 c 的顏色,跟關(guān)注節(jié)點 a 的父節(jié)點 b 設(shè)置為相同的顏色;
- 將關(guān)注節(jié)點 a 的父節(jié)點 b 的顏色設(shè)置為黑色;
- 從關(guān)注節(jié)點 a 中去掉一個黑色,節(jié)點 a 就變成單純的紅色或黑色;
- 將關(guān)注節(jié)點 a 的叔叔節(jié)點 e 設(shè)置為黑色;
- 調(diào)整結(jié)束。
三、TIPs
- 找準(zhǔn)關(guān)注節(jié)點,不要搞丟、搞錯關(guān)注節(jié)點
總結(jié)
- 上一篇: 十二、二叉树
- 下一篇: 十四、堆(Heap)