文章目錄
數據結構與算法——紅-黑樹
1 爲什麼需要紅-黑樹?
普通二叉搜索樹,插入新節點後可能導致樹的不平衡。
而平衡的二叉樹,才能使搜索效率最高。
紅-黑樹在插入和刪除的時候進行了一些特殊的處理,能使二叉樹保持平衡。
2 紅-黑規則
紅-黑樹在插入(刪除)一個節點時,只有遵循紅-黑規則,樹才能始終保持平衡。
即 遵循紅-黑規則<=>平衡樹,把平衡樹的問題轉變爲讓樹遵循紅-黑規則的問題。
① 每一個節點不是紅色的就是黑色的。
② 根總是黑色的。
③ 如果節點是紅色的,則它的子節點必須是黑色的(反之倒不一定必須爲真)。
④ 從根到葉節點或空子節點的每條路徑,必須包含相同數目的黑色節點(即黑色高度相同)。
3 爲什麼默認插入紅色節點?
- 若默認插入紅色節點
- 若父節點是黑節點——>不違反紅-黑規則
- 若父節點是紅節點——>違法紅-黑規則③
- 若默認插入黑色節點
- 無論父節點是黑或紅——>一定違反紅-黑規則④
綜上,默認插入紅色節點違規率50%,默認插入黑色節點違規率100%,所以默認插入紅色節點好。
4 如何修正違規?
(1) [三節點]顏色變換
- 觸發:在插入過程中遇到一個黑色節點,並且它還有兩個紅色子節點,則需要三節點顏色變換。
- 目的:在不違反紅-黑規則④的前提下,一是使連接紅色節點更方便,二是保證在進行一次或兩次旋轉操作後整個樹仍是正確的紅-黑樹,三是保證了插入時若父節點(P節點)是紅色節點則不可能有兄弟節點(不可能有一個紅色兄弟節點,因爲有顏色變換;不可能有一個黑色兄弟節點,因爲違背紅-黑規則④)。
- 缺點:若祖父節點爲紅色節點,則會違反紅-黑規則③。
- 情況
- 情況1:該黑色節點是根節點。
- 情況2:該黑色節點不是根節點。
(2) 單節點顏色改變
- 觸發:插入一個新紅色節點後,而它的父節點也是紅色節點,則需要單節點顏色改變(違背了紅-黑規則③)。
- 目的:單節點顏色改變和旋轉一起修正父節點和子節點都是紅色的違規問題,廣義的旋轉包括“單節點顏色改變”和“旋轉”。
- 情況:
(3) 旋轉
- 觸發:插入新節點前或插入新節點後違反紅-黑規則③,則需要旋轉。
- 目的:單節點顏色改變和旋轉一起修正父節點和子節點都是紅色的違規問題。
- 說明:
① 旋轉分爲左旋轉和右旋轉,以某個節點爲頂點,向左或右“旋轉”。
② 頂點的子節點和外側子孫節點會跟隨旋轉方向上移或下移。
③ 橫向移動節點:頂點的內側子孫節點若是上移節點,則會斷開與父節點的連接並且連接到它以前的祖父節點上;若原來是父節點的右子節點橫向移動後會變成祖父節點的左子節點。 - 注意:紅黑規則、三節點顏色變換和單節點顏色改變都只是有助於何時執行旋轉,旋轉纔是平衡樹起關鍵作用的操作。
5 插入一個新節點
步驟:向下路途中的顏色變換——>向下路途中的旋轉——>插入新節點——>插入節點之後的旋轉
說明:G——祖父節點、P——父節點、X——新插入節點
(1) 向下路途中的顏色變換
- 即三節點顏色變換。
- “向下路途中的旋轉”是用來解決“向下路途中的顏色變換”可能帶來的紅色父節點與紅色子節點連接的違規問題,把它放到最後分析比較合適。
(3) 插入新節點
- 改變父節點引用即可,不再贅述。
(4) 插入節點之後的旋轉
-
新插入節點有四種指向變化
-
插入節點之後的三種可能性情況
-
可能性1:P是黑色的(不會違背紅-黑規則)
- 什麼都不需要做。
-
可能性2:P是紅色的,X是G的一個外側子孫點(出現紅色節點與紅色節點的連接,違背紅-黑規則③)
- 單節點改變顏色1:改變G節點的顏色;
- 單節點改變顏色2:改變P節點的顏色;
- 旋轉1:以G節點爲頂向X節點上升的方向旋轉。
-
可能性3:P是紅色的,X是G的一個內測子孫點(出現紅色節點與紅色節點的連接,違背紅-黑規則③)
- 單節點改變顏色1:改變G節點的顏色;
- 單節點改變顏色2:改變X節點的顏色;
- 旋轉1:以P節點爲頂向X節點上升的方向旋轉(轉化成了“可能性2:P是紅色的,X是G的一個外側子孫點”的情況);
- 旋轉2:以G節點爲頂向X節點上升的方向旋轉。
-
-
其他情況分析
- 若X節點有兄弟節點S
- 若P節點爲黑色,則X節點直接連接到P節點(根據紅-黑規則④,S節點一定是紅色,但對X節點的插入無影響)。
- 若P節點爲紅色,根據紅-黑規則③S節點一定是黑色的。已知插入前和插入後樹一定是平衡且符合紅-黑規則的,但是插入前P節點卻有單個黑色子節點S,不符合紅-黑規則④,因此這種情況是不存在的。
- 若P節點有兄弟節點U
- 若P節點爲黑色,則X節點直接連接到P節點(根據紅-黑規則④,U節點一定是黑色,但對X節點的插入無影響)。
- 若P節點是紅色,根據紅-黑規則④U節點一定是紅色的。但是有兩個紅色子節點的黑色父節點在沿着路徑向下的時候就顏色變換了,因此這種情況是不存在的。
- 綜上,上面討論的三種可能性情況(可能性1、可能性2和可能性3)是全部可能存在的插入情況。
- 若X節點有兄弟節點S
(2) 向下路途中的旋轉
- “向下路途中的旋轉”是用來解決“向下路途中的顏色變換”可能帶來的紅色父節點與紅色子節點連接的違規問題,有“X節點是外側子孫節”點和“X節點是內側子孫節點”兩種情況。
此時不必糾結“X節點”是什麼,下文將會說明。 - X節點是外側子孫節點:
- S1-情形:當前二叉樹如下,待插入值3,節點3應該插在節點6的左子節點。
- S2-從根節點向下走,走到節點12發現需要對其及子節點進行顏色變換,變換後發現節點12與其父節點25違反了紅-黑規則③。
- S3-此時把節點12記作X,X的父節點25記作P,X的祖父節點50記作G,接下來仿照(4)中的可能性2:P是紅色的,X是G的一個外側子孫點情況進行操作。
- S3-1-將G節點和P節點顏色改變。
- S3-2-以G節點爲頂點,進行X節點向上方向的旋轉(此處是右旋)。發現樹平衡了!
- S4-重新從根節點25向下走,去尋找合適的位置插入節點3。發現根節點25有兩個紅色子節點,需要進行顏色變換。
- S5-找到合適的位置插入節點3,結束。
- S1-情形:當前二叉樹如下,待插入值3,節點3應該插在節點6的左子節點。
注意:上文展示了“X節點是外側子孫節點”情況下的一個完整的插入過程,其中整個S3部分纔是“向下途中的旋轉”。
- X節點是內側子孫節點:
- S1-情形:當前二叉樹如下,待插入值28,節點28應該插在節點31的左子節點。
- S2-從根節點向下走,走到節點37發現需要對其及子節點進行顏色變換。變換後發現節點37與其父節點25違反了紅-黑規則③。
- S3-此時把節點37記作X,X的父節點25記作P,X的祖父節點50記作G,接下來仿照(4)中的可能性3:P是紅色的,X是G的一個內測子孫點情況進行操作。
- S3-1-將X節點和G節點改變顏色。
- S3-2-以P節點爲頂點,進行X節點向上方向的旋轉(此處是左旋)。
- S3-3-再以G節點爲頂點,進行X節點向上方向的旋轉(此處是右旋)。發現樹平衡了!
- S4-從根節點37向下走,去尋找合適的位置插入節點28。發現根節點37有兩個紅色子節點,進行顏色變換。
- S5-找到合適的位置插入節點28,結束。
- S1-情形:當前二叉樹如下,待插入值28,節點28應該插在節點31的左子節點。
注意:上文展示了“X節點是內側子孫節點”情況下的一個完整的插入過程,其中整個S3部分纔是“向下途中的旋轉”。
6 刪除
- 刪除過程過於複雜,很多程序員都用不同的方法迴避它,一種方法是爲刪除節點做個標記而不實際地刪除它。
7 效率
- 查找:O(logN)——查找方法和普通二叉搜索樹一樣,效率相同。
- 插入和刪除:O(logN)——相對於普通二叉搜索樹,增加了一個常數因子(顏色變換和旋轉的時間消耗),效率略低於普通二叉搜索樹。
參考
[1] Java數據結構和算法(第二版)