一、紅黑樹的介紹
-
通過對任何一條從根到葉子的路徑上各個結點着色方式的限制,紅黑樹確保沒有一條路徑會比其他路徑長出倆倍,因而是接近平衡的。
-
在二叉查找樹的基礎上增加了着色和相關的性質使得紅黑樹相對平衡,從而保證了紅黑樹的查找、插入、刪除的時間複雜度最壞爲O(logn)。
二、二叉查找樹的回顧
(1)二叉查找樹的介紹
-
二叉查找樹也稱有序二叉樹,或已排序二叉樹;
-
樹的這種數據結構,既能像鏈表那樣快速的插入和刪除,又能想有序數組那樣快速查找。
(2)二叉查找樹的基本特徵
- 若任意節點的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
- 若任意節點的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
- 沒有鍵值相等的節點;
- 需要保證樹總是平衡的(或者至少大部分是平衡的),這就是說對樹中的每個節點在它左邊的後代數目和在它右邊的後代數目應該大致相等
三,紅黑樹的基本特性
- 每個結點要麼是紅的,要麼是黑的。
- 根結點是黑的。
- 每個葉結點,即空結點(NIL)是黑的。
- 如果一個結點是紅的,那麼它的倆個兒子都是黑的。
(如果一個結點是黑的,則它的倆個兒子不作要求) - 從根節點到葉節點或空子節點的每條路徑,必須包含相同數目的黑色節點。
(故需要特別注意黑色節點的設置)
四,紅黑樹平衡性的修正
-
部分結點顏色,重新着色
-
調整部分指針的指向,即左旋、右旋。
1. 左旋
2. 右旋
五、紅黑樹的插入和插入修復
-
核心思路:將紅色的節點移到根節點(即將出現的問題由下向上拋);然後,將根節點設爲黑色
-
插入的節點必須是紅色的。將插入的節點着色爲紅色,不會違背"特性(5)"!少違背一條特性,就意味着我們需要處理的情況越少。
第一步:
將紅黑樹當作一顆二叉查找樹,將節點插入。從樹根開始向下查找,找到val值對應的位置,將節點插入;如果查找到與val值相同的結點,則什麼也不做,直接返回
第二步:
將插入的節點着色爲"紅色"。
第三步:
通過一系列的旋轉或着色等操作,使之重新成爲一顆紅黑樹。即插入修復。
情況1:插入節點z的父節點是黑色。
解法:什麼也不需要做。節點被插入後,仍然是紅黑樹。
情況2:插入節點z是根節點。
解法:直接把此節點塗爲黑色。
情況3:插入節點z的父結點是紅色的,叔叔結點也是紅色的。
解法:
(a)將叔叔塗黑;
(b)將父結點塗黑;
(c)將祖父節點塗成紅色;
(d)當前節點z的祖父節點成爲新的當前節點z。
變化前(當前結點4):
變化後:
思路分析:
- 當前節點4和父節點5都是“紅色”,違背“特性(4)”。解決方法是:將父節點5設置“黑色”
- 但是,將父節點5變成“黑色”後,父節點所在分支的“黑色”節點的總數增加了1,違背了“特性(5)”。 解決辦法是:將祖父節點7變成“紅色”,同時,將叔叔節點8變成“黑色”。
- 當前節點4、父節點5、叔叔節點8之間都不會違背紅黑樹特性,但祖父節點7卻不一定。解決方法是:若此時,祖父節點7是根節點( 即二叉樹爲{ 7,5,8,4} ),直接將祖父節點7設爲“黑色”;若祖父節點7不是根節點,那我們需要將祖父節點7設爲新的當前節點,接着對新的當前節點7進行分析。
- 破壞紅黑樹特性的節點由節點4變成節點7
情況4:插入節點z的父結點是紅色的,叔叔結點是黑色的,且x是右孩子。
解法:
(a)當前節點z的父結點成爲新的當前結點z;
(b)以新當前結點爲支點進行左旋;
變化前(當前結點7):
變化後:
思路分析:
-
若此時父結點2是根節點(即二叉樹爲{ 2,1,7,5,8,4}),直接將父結點2塗爲黑色即可。
-
若此時父結點2不是根節點,則需要對當前結點7做進一步的分析。
- 如果將父節點2染成黑色,以遵循“特性(4)”,那麼父節點2所在分支的“黑色”節點的總數增加了1,違背“特性(5)”,那麼我們將祖父節點11染成紅色,此時違背了“特性(5)和特性(2)”,故以祖父節點11爲支點進行右旋,但此時根節點2的左孩子1是黑色的,違背了“特性(4)”,故該方式不可行。
- 如此看來,似乎情況4陷入了死局。那麼,讓我們來回顧一下紅黑樹插入的核心思路:**將紅色的節點移到根節點;然後,將根節點設爲黑色。**既然是“將紅色的節點移到根節點”,那就是說要不斷將破壞紅黑樹特性的紅色節點(即當前結點)上移(即向根方向移動), 而結點7又是結點2右孩子。解決方法是:通過“左旋”來將破壞紅黑樹特性的“紅色”節點7上移!
- 破壞紅黑樹特性的節點7上移,破壞紅黑樹特性的節點由節點7變爲節點2
- 從另一個角度看,若發生情況4且當前結點的父結點不是根結點的話,我們就將情況4通過左旋轉化爲情況5。
情況5:插入節點z的父結點是紅色的,叔叔結點是黑色的,且x是左孩子。
解法:
(a)將父結點塗黑
(b)將祖父節點塗紅
(c)以祖父節點爲支點進行右旋
變化前(當前結點2):
變化後:
思路分析:
-
若此時父結點7是根節點(即二叉樹爲{ 7,2,8,1,5,4}),直接將父結點7塗爲黑色即可。
-
若此時父結點7不是根節點,則我們需要作進一步分析。
-
當前節點2和父結點7都是“紅色”,違背了紅黑樹的“特性(4)”。解決方法:將父結點7由“紅色”變爲“黑色”
-
但父結點7由紅色改爲黑色之後,所有經過F的分支的黑色節點的個數增加了1,違背了紅黑樹的“特性(5)”。
解決方法:“將祖父節點11由黑色變成紅色”,同時“以祖父節點11爲支點進行右旋”來解決。 -
破壞紅黑樹特性的節點2上移。
六、紅黑樹的刪除和刪除修復
第一步:
將紅黑樹當作一顆二叉查找樹,將節點刪除。
- 被刪除節點z沒有兒子,即爲葉節點。那麼,令y=z,直接刪除y節點就可以了。
- 被刪除節點z只有一個兒子。那麼,令y=z,刪除y。並用該節點的唯一子節點頂替它的位置,即y必定有唯一的一個右孩子或左孩子,y的孩子與y的父親應當建立關係。
- 被刪除節點z有兩個兒子。那麼,先找出它的後繼節點y;然後把“它的後繼節點y的內容”複製給“節點z的內容”;之後,刪除“它的後繼節點”y。此時,y可能有右孩子(不可能有左孩子),若y有右孩子,y的右孩子與y的父親應當建立關係。
第二步:
通過"旋轉和重新着色"等一系列來修正該樹,使之重新成爲一棵紅黑樹。即刪除修復。
如果刪除的是紅色節點,那麼原紅黑樹的性質依舊保持,此時不用做修正操作,如果刪除的節點是黑色節點,原紅黑樹的性質可能會被改變,我們要對其做修正操作。以下內容是刪除節點爲黑色的修復情況。
情況1:當前節點x的兄弟w是紅色的。
解法:
(a) 把兄弟節點塗成黑色;
(b)父結點塗成紅色;
(c)以當前結點的父結點爲支點進行左旋;
(d)x的兄弟節點發生變化,要重新定位
變化前:
變化後:
思路分析:
- 把問題轉換成兄弟節點爲黑的情況(即情況2,3,4)。解決方法:以D爲節點進行左旋
- 根節點D是紅色的,違反“規則2”。解決方法:將D節點塗成黑色
- 包含“B節點”的分支的黑色節點的總數增加了1,違反“規則5”。解決方法:將B節點塗成紅色。
情況2:當前節點x的兄弟w是黑色的,且w的倆個孩子都是黑色的。
解法:
(a)將兄弟節點w塗成紅色;
(b)將當前結點x的父結點作爲新的當前結點x
變化前:
變化後:
思路分析:
- 若刪除了當前結點 x(即結點),因爲 x 子樹相對於其兄弟子樹 w 少一個黑色節點,可以將w置爲紅色,這樣,x子樹與w子樹黑色節點一致,保持了平衡。
- 將 x 結點的父結點(即B結點)作爲新的當前節點 new x。new x相對於它的兄弟子樹new w少一個黑色結點。
(實質是將不平衡點上移至x的父親,進行下一輪迭代) - 如果new x爲紅色,則將new x置爲黑,則整棵樹平衡。
- 如果new x爲黑色的,則要根據情況重新進入情況1,2,3,4
情況3:當前節點x的兄弟w是黑色的,且w的左子是紅色,w的右子是黑色。
解法:
(a)把兄弟節點塗成紅色;
(b)把兄弟節點的左子節點塗黑;
(c)以兄弟節點爲支點做右旋操作;
(d)x的兄弟節點發生變化,要重新定位
變化前:
變化後:
思路分析:
- 把情況3轉換爲情況4
情況4:當前節點x的兄弟w是黑色的,且w的右子是紅色的,w左子顏色任意。
解法:
(a)把兄弟結點塗成和父結點相同的顏色;
(b)將父親結點染成黑色;
(c)將兄弟結點的右孩子染成黑色;
(d)以當前結點的父結點爲支點進行左旋;
(e)x的兄弟節點發生變化,要重新定位。
變化前:
變化後:
思路分析:
- 兄弟節點的右兒子是紅色節點,那麼將右兒子染成黑色,從整體上補上缺失的黑色。
- 再通過旋轉,補回 x 子樹中因爲刪除黑色節點而丟失的黑色。
七、紅黑樹與AVL樹的比較
-
如果插入一個node引起了樹的不平衡,AVL和RB-Tree都是最多只需要2次旋轉操作,時間複雜度O(1)。
-
如果刪除一個node引起了樹的不平衡,最壞情況下,AVL需要維護從被刪node到root這條路徑上所有node的平衡性,時間複雜度O(logN),而RB-Tree最多隻需3次旋轉,時間複雜度O(1)。
-
其次,AVL的結構相較RB-Tree來說更爲平衡,在插入和刪除node更容易引起Tree的unbalance,因此在大量數據需要插入或者刪除時,AVL需要rebalance的頻率會更高。
-
map的實現只是折衷了兩者在search、insert以及delete下的效率。總體來說,RB-tree的統計性能是高於AVL的。
因此,在需要大量插入和刪除node的場景下,RB-Tree效率更高。而AVL由於高度平衡,search的效率更高,適用於插入刪除node少,查詢多的場景。
源代碼地址:
https://github.com/Zheng-Wenkai/DataStructure.git
參考內容:
http://www.cnblogs.com/skywang12345/p/3624177.html
http://blog.csdn.net/v_JULY_v/article/details/6105630
https://www.zhihu.com/question/20545708/answer/58717264