1. 程式人生 > >資料結構之紅黑樹(二)——插入操作

資料結構之紅黑樹(二)——插入操作

插入或刪除操作,都有可能改變紅黑樹的平衡性,利用顏色變化與旋轉這兩大法寶就可應對所有情況,將不平衡的紅黑樹變為平衡的紅黑樹。

在進行顏色變化或旋轉的時候,往往要涉及祖孫三代節點:X表示操作的基準節點,P代表X的父節點,G代表X的父節點的父節點。

我們先來大體預覽一下插入的過程:

1、沿著樹查詢插入點,如果查詢過程中發現某個黑色節點的兩個子節點都是紅色,則執行一次顏色變換(父節點變為紅色,而兩個紅色子節點變為黑色)。

2、第1步中,不會改變子樹的黑色高度,但是可能會出現顏色衝突(紅-紅顏色衝突),執行一次或兩次旋轉即可解決。設紅色子節點為X,紅色父節點為P,旋轉次數由X是G的內側子孫還是外側子孫決定。

3、找到插入點之後,設X為新插入的節點。如果P是黑色的,則不需要做任何改變,插入完成。

4、如果P是紅色的,則發生了紅-紅顏色衝突,需要做兩次顏色變化,如果X為G的外側子孫,再進行一次旋轉;如果X為G的內側子孫,再進行兩次旋轉。最終都可使樹變為平衡的紅黑樹。

現在看不懂沒關係,為何要這麼做,我們接下來慢慢分析。

第1步與第2步看似與插入新節點沒關係,其實為了給新節點的插入掃清道路,到後面插入新節點時就會體現出來。

先來看第1步的詳細過程:


上圖中,查詢到P點,發現它的兩個子節點都是紅色,則進行顏色變換(如果P是根,則保持黑色不變)。這種變換並不會改變從根節點經P到葉節點或者空節點的路徑上的黑色節點總數,即不會改變其黑色高度。將P、X1、X2看做三角形的三個頂點,顏色變換之前,經過此三角形時會增加一個黑色節點,顏色變換之後,P變成了紅色,X1、X2變成了黑色,不論是經過X1還是經過X2,還是會增加一個黑色節點。

如果P的父節點是黑色,則不會出現任何問題,但是,如果P的父節點也是紅色,就會發生紅-紅顏色衝突,需要通過旋轉來修正。發生顏色衝突時有兩種情況需要區別對待。

注意,這時候我們選定紅-紅顏色衝突父子節點中的子節點作為基準節點,即X。如果X在P的一側與P在G的一側相同,X即為G的外側子孫,反之,則為內側子孫

情況1:X為外側子孫節點。


上圖中,表示的是顏色變換之後的情況,12跟25節點發生了顏色衝突,12為50的外側子孫。

在這種情況需要採取三步操作:

1、改變G的顏色;

2、改變P的顏色

3、以G為中心進行向X上升的方向旋轉(本例中是右旋)。


奇蹟發生了,樹突然之間平衡了,而且是符合紅黑規則的。

需要注意的是,在本例中,由於25是50的左子節點,進行的是右旋操作,加入它是右子節點,則需要進行左旋操作。無論是左旋還是右旋,都是向著X上升的方向旋轉

情況2:X為內側子孫節點。

修正這種情況比較複雜一點,如果我們採取跟內側子孫一樣的做法,X不會上移而是發生橫向移動,使樹變得更加不平衡。因此需要一種不同的方法來解決。

我們先要用一次旋轉讓X成為外側子孫,然後再用一次旋轉使樹平衡。

這種情況需要進行四步操作:

1、改變G的顏色;

2、改變X的顏色;

3、以P為中心向X上升的方向旋轉;

4、以G為中心向X上升的方向旋轉。

 

至此,前期工作已經完成,下面進行新節點的插入。在插入環節,我們以新節點為基準點,即X。

在前面已經說過,我們總是預設新節點為紅色。那麼,找到插入點的時候,會有兩種情況,一種是X的父節點為P為黑色,直接插入即可(因為插入一個紅色新節點既不會影響樹的黑色高度,也不會發生顏色衝突);另一種情況是X的父節點P也為紅色,插入後會發生紅-紅顏色衝突,需要通過顏色變換與旋轉來修正。

發生顏色衝突的時候,根據X是內側子孫還是外側子孫分別對待,處理方法與上面提到的方法類似。

外側子孫:

內側子孫:

下面我們來討論一下,是否還有其他情況。

假如X有一個兄弟節點S,即P的另一個子節點,會使任何需要的旋轉更加複雜。如果P為黑色,無論X有沒有兄弟節點,都不需要旋轉;如果P為紅色,則插入之前,P不可能有一個單獨的黑色子節點,因為這樣會使S和空子節點的黑色高度不一樣。綜上,插入新節點之後,不會出現X存在兄弟節點而且需要旋轉修正的情況。

假如P有一個兄弟節點,即X的叔節點U,也會使任何需要的旋轉更加複雜。如果P為黑色,X插入後不要要做任何旋轉;如果P為紅色,則U必須為紅色,否則,G到P的黑色高度與G到U的黑色高度就不同了。但是,有兩個紅色子節點的父節點在插入之前我們已經處理掉了,所以這種情況也不會存在。綜上,插入新節點之後,不會出現P存在兄弟節點且需要旋轉修正的情況。

到現在,就明白為什麼要在尋找插入點的過程中,把有兩個紅色子節點的父節點的顏色變換掉,一方面是為了使樹更加平衡,另一方面是大大簡化了插入後的旋轉操作。