紅黑樹(Red Black Tree)
介紹另一種平衡二叉樹:紅黑樹(Red Black Tree),紅黑樹由Rudolf Bayer於1972年發明,當時被稱為平衡二叉B樹(symmetric binary B-trees),1978年被Leonidas J. Guibas 和 Robert Sedgewick改成一個比較摩登的名字:紅黑樹。
紅黑樹和之前所講的AVL樹類似,都是在進行插入和刪除操作時通過特定操作保持二叉查詢樹的平衡,從而獲得較高的查詢效能。自從紅黑樹出來後,AVL樹就被放到了博物館裡,據說是紅黑樹有更好的效率,更高的統計效能。不過在我瞭解了紅黑樹的實現原理後,並不相信這是真的,關於這一點我們會在後面進行討論。
紅黑樹和
首先來一個Silverlight做的紅黑樹的動畫,它有助於幫你理解什麼是紅黑樹。這裡需要注意,必須安裝Silverlight 2.0 RTW 才能正常運行遊戲,下載地址:
使用注意事項:
l結點只接收整數,如果在新增和刪除操作中輸入非法字串,則會隨機新增或刪除一個0~99之間的整數。
l可以不在編輯框中輸入數字,直接單擊新增和刪除按鈕進行新增和刪除操作。
l可以拖動拖動條控制動畫速度。
紅黑樹的平衡
紅黑樹首先是一棵二叉查詢樹,它每個結點都被標上了顏色(紅色或黑色),紅黑樹滿足以下5個性質:
1、 每個結點的顏色只能是紅色或黑色。
2、 根結點是黑色的。
3、 每個葉子結點都帶有兩個空的黑色結點(被稱為黑哨兵),如果一個結點n的只有一個左孩子,那麼n的右孩子是一個黑哨兵;如果結點n只有一個右孩子,那麼n的左孩子是一個黑哨兵。
4、 如果一個結點是紅的,則它的兩個兒子都是黑的。也就是說在一條路徑上不能出現相鄰的兩個紅色結點。
5、 對於每個結點來說,從該結點到其子孫葉結點的所有路徑上包含相同數目的黑結點。
紅黑樹的這5個性質中,第3點是比較難理解的,但它卻非常有必要。我們看圖
要看真正的紅黑樹請在以上動畫中新增幾個結點,看看是否滿足以上性質。
紅黑樹的旋轉操作
紅黑樹的旋轉操作和AVL樹一樣,分為LL、RR、LR、RL四種旋轉型別,差別在於旋轉完成後改變的是結點的顏色,而不是平衡因子。旋轉動畫演示請參考AVL這篇文章中的Flash動畫:
http://www.cnblogs.com/abatei/archive/2008/11/17/1335031.html
紅黑樹上結點的插入
在討論紅黑樹的插入操作之前必須要明白,任何一個即將插入的新結點的初始顏色都為紅色。這一點很容易理解,因為插入黑點會增加某條路徑上黑結點的數目,從而導致整棵樹黑高度的不平衡。但如果新結點父結點為紅色時(如圖2所示),將會違返紅黑樹性質:一條路徑上不能出現相鄰的兩個紅色結點。這時就需要通過一系列操作來使紅黑樹保持平衡。
為了清楚地表示插入操作以下在結點中使用“新”字表示一個新插入的結點;使用“父”字表示新插入點的父結點;使用“叔”字表示“父”結點的兄弟結點;使用“祖”字表示“父”結點的父結點。插入操作分為以下幾種情況:
1、黑父
如圖3所示,如果新點的父結點為黑色結點,那麼插入一個紅點將不會影響紅黑樹的平衡,此時插入操作完成。紅黑樹比AVL樹優秀的地方之一在於黑父的情況比較常見,從而使紅黑樹需要旋轉的機率相對AVL樹來說會少一些。
2.紅父
如果新點的父結點為紅色,這時就需要進行一系列操作以保證整棵樹紅黑性質。如圖3所示,由於父結點為紅色,此時可以判定,祖父結點必定為黑色。這時需要根據叔父結點的顏色來決定做什麼樣的操作。青色結點表示顏色未知。由於有可能需要根結點到新點的路徑上進行多次旋轉操作,而每次進行不平衡判斷的起始點(我們可將其視為新點)都不一樣。所以我們在此使用一個藍色箭頭指向這個起始點,並稱之為判定點。
2.1 紅叔
當叔父結點為紅色時,如圖4所示,無需進行旋轉操作,只要將父和叔結點變為黑色,將祖父結點變為紅色即可。但由於祖父結點的父結點有可能為紅色,從而違反紅黑樹性質。此時必須將祖父結點作為新的判定點繼續向上進行平衡操作。
需要注意,無論“父”在“叔”的左邊還是右邊,無論“新”是“父”的左孩子還是右孩子,它們的操作都完全一樣。
2.2 黑叔
當叔父結點為黑色時,需要進行旋轉,以下圖示了所有的旋轉可能
情形1:
情形2:
情形3:
情形4:
可以觀察到,當旋轉完成後,新的旋轉根全部為黑色,此時不需要再向上回溯進行平衡操作,插入操作完成。需要注意,上面四張圖的“叔”、“1”、“2”、“3”結點有可能為黑哨兵結點。
其實紅黑樹的插入操作不是很難,甚至比AVL樹的插入操作還更簡單些。但刪除操作就遠遠比AVL樹複雜得多,下面就介紹紅黑樹的刪除操作。
紅黑樹上結點的刪除
紅黑樹本身是一棵二叉查詢樹,它的刪除和二叉查詢樹的刪除類似。首先要找到真正的刪除點,當被刪除結點n存在左右孩子時,真正的刪除點應該是n的中序遍在前驅,關於這一點請複習二叉查詢樹的刪除。如圖9所示,當刪除結點20時,實際被刪除的結點應該為18,結點20的資料變為18。
所以可以推斷出,在進行刪除操作時,真正的刪除點必定是隻有一個孩子或沒有孩子的結點。而根據紅黑樹的性質可以得出以下兩個結論:
1、 刪除操作中真正被刪除的必定是隻有一個紅色孩子或沒有孩子的結點。
2、 如果真正的刪除點是一個紅色結點,那麼它必定是一個葉子結點。
理解這兩點非常重要,如圖10所示,除了情況(a)外,其他任一種況結點N都無法滿足紅黑樹性質。
在以下討論中,我們使用藍色箭頭表示真正的刪除點,它也是旋轉操作過程中的第一個判定點;真正的刪除點使用“舊”標註,舊點所在位置將被它的的孩子結點所取代(最多隻有一個孩子),我們使用“新”表示舊點的孩子結點。刪除操作可分為以下幾種情形:
1、舊點為紅色結點
若舊點為紅色結點,則它必定是葉子結點,直接刪除即可。如圖11所示
2、一紅一黑
當舊點為黑色結點,新點為紅色結點時,將新點取代舊點位置後,將新點染成黑色即可(如圖12所示)。這裡需要注意:舊點為紅色,新點為黑色的情況不可能存在。
3、雙黑
當舊點和新點都為黑色時(新點為空結點時,亦屬於這種情況),情況比較複雜,需要根據舊點兄弟結點的顏色來決定進行什麼樣的操作。我們使用“兄”來表示舊點的兄弟結點。這裡可分為紅兄和黑兄兩種情況:
3.1 紅兄
由於兄弟結點為紅色,所以父結點必定為黑色,而舊點被刪除後,新點取代了它的位置。下圖演示了兩種可能的情況:
紅兄的情況需要進行RR或LL型旋轉,然後將父結點染成紅色,兄結點染成黑色。然後重新以新點為判定點進行平衡操作。我們可以觀察到,旋轉操作完成後,判定點沒有向上回溯,而是降低了一層,此時變成了黑兄的情況。
3.2 黑兄
黑兄的情況最為複雜,需要根據黑兄孩子結點(這裡用“侄”表示)和父親結點的顏色來決定做什麼樣的操作。
3.2.1 黑兄二黑侄紅父
如圖14所示,這種情況比較簡單,只需將父結點變為黑色,兄結點變為黑色,新結點變為黑色即可,刪除操作到此結束。
3.2.2 黑兄二黑侄黑父
如圖15所示,此時將父結點染成新結點的顏色,新結點染成黑色,兄結點染成紅色即可。當新結點為紅色時,父結點被染成紅色,此時需要以父結點為判定點繼續向上進行平衡操作。
3.2.3 黑兄紅侄
黑兄紅侄有以下四種情形,下面分別進行圖示:
情形1:
情形2:
情形3:
情形4:
由以上圖例所示,看完以上四張圖的兄弟有可能會有一個疑問,如果情形1和情形2中的兩個侄子結點都為紅色時,是該進行LL旋轉還是進行LR旋轉呢?答案是進行LL旋轉。情形3和情形4則是優先進行RR旋轉的判定。