1. 程式人生 > >紅黑樹學習筆記之插入操作

紅黑樹學習筆記之插入操作

紅黑樹是234樹的一種,它是自平衡的二叉查詢樹,其優點為可以在㏒(n)時間內完成查詢,刪除和插入的動作。

性質1:節點為有色的(紅色和黑色)

性質2:根節點以及所有的葉子節點為黑色

性質3:所有的紅色節點的父節點為黑色節點(黑色節點的父節點可以為紅色也可以為黑色)

性質4:從任意節點到其簡單路徑的黑色節點數相同

紅黑樹簡單例項:

注意到葉子節點都是沒有資料的,在紅黑樹中葉子節點為沒有資料的黑色節點(滿足性質2),以根節點為例到任意的葉子節點的黑色節點為3(滿足性質4),根節點為黑色且所有節點都為有色節點滿足性質1,2。前面提到紅黑樹是自平衡的二叉查詢樹,

所以上圖有二叉樹的影子。現在以這棵樹為例插入一個15的值,根據二叉查詢樹的性質15>7<18>10>11所以插入位置為11的右孩子的位置,確定插入位置以後還要給他染色(只有紅黑可選不可以染成綠色微笑

)那麼既然可以選紅黑怎麼選?其實紅黑都可以選,但是為了簡單起見我們插入一個數據的時候都染成紅色。於是我們就有了這樣一幅圖:


現在請看性質3:所有紅色節點的父節點為黑色節點,於是我們違背了性質3;所以我們就要修改一下我們的樹使得他繼續遵守所有的性質。通常處理這個問題我們有兩種辦法:其一是變色,其二是旋轉。變色也就是改變節點的顏色使得所有節點滿足規律,比如我們可以把15變成黑色,當然這沒什麼用,我們需要將一些其他的節點變色。旋轉操作:旋轉分為左旋轉和右旋轉,當然原理是一樣的,先來看一個左旋轉:


旋轉原理:根據二叉樹的特性有: b < a; b > c; b > d; a < e; 由此可得 d < a;旋轉90度以後大小關係保持不變因此d節點需要變為a節點的左孩子(b < d < a),而 a 變為b的有孩子(a > b);右旋轉同理可得。

       先來操作一下我們的紅黑樹:觀察一下發現新插入的節點祖父節點(節點10)為黑色,而父節點和叔叔節點(暫且這麼叫吧,畢竟是父親的兄弟節點)都是紅色節點,這種情況直接將父節點叔叔節點都變色就好了:變色完畢是這樣的情況:


nice!!!你會發現11和15沒有了衝突,但是10和18又遇到了這個問題!!!WHAT?沒辦法只好繼續看了,10的祖父節點為7,這是根節點,顯然我們不能讓根節點變紅啊,如果讓根節點變紅我們就又多違反了一個性質了不太好。所以這裡我們要對這個樹進行旋轉了,圍繞哪裡旋轉呢?問題在哪就圍繞哪!沒錯就是10-18這條軸,旋轉一下得到:

但是問題還是沒有解決啊!10-18仍然是兩個紅色的連在一起的,這個時候我們還需要一次旋轉,找哪個軸呢?自然是出了問題的軸,10-18已經旋轉過了,所以這裡我們只能找下一個,也就是10的上一層,我們圍繞7-10軸做一個右旋轉可以得到下面的圖形:


這一步看上去已經快要差不多了,但是還是違背了性質並且這裡還多違背了一個,其實這一步本來應該是做兩件事的但是這裡分開來看,先是進行旋轉操作隨後進行變色操作,將旋轉軸上面的兩個節點進行變色也就是7-10上面的兩個都變色,紅變黑黑變紅,最終結果:


現在檢查一下:四個性質還滿足不,第一,二很顯然滿足,第三個也滿足,第四個遍歷一下發現也是滿足的,因此我們通過這些變色和旋轉使得這棵樹維持了自平衡,great!!!

但是我們前面只是通過觀察得到什麼時候要變色什麼時候要旋轉,但是真實的情況可不是這樣的程式碼是不會觀察的我們要給他一個確定的情況通知他該幹啥,而且我們自己也要弄清楚什麼時候該旋轉什麼時候該變色。這裡我們其實分兩種情況:第一種,樹的分支(我們需要進行處理的分支)為左偏,另一種就是右偏了。這裡就說左偏,右偏即可同理得到了。左偏分為三種情況:

1.祖父節點為黑色,且父節點和叔叔節點為紅色:


黑色方塊中可能有很多子節點我們不關心他因為根據紅黑樹的性質這些節點中的黑色節點是相等的,遇到這種情況我們需要做的就是將祖父節點與父節點以及叔叔節點變色:


如果下面還是這種情況遞迴處理變色就好;

第二種:祖父節點為黑色,父節點為紅色且叔叔節點為黑色:


這種情況我們進行旋轉處理:


這就得到了我們的第三種情況;

第三種情況:


跟上圖差不多,這種情況就需要對父節點以及祖父節點進行旋轉並且變色的操作,左旋轉,並且父節點與祖父節點的顏色都要進行變色處理,處理後的結果如下:


通常處理到這一步都可以完成插入了。樹的旋轉和變色的時間為O(1)但是變色操作最多會迴圈㏒(n)次因此紅黑樹的插入操作時間複雜度為O(㏒(n))。

        以上為本人學習的筆記,如果有大佬發現有錯請耐心指出,感激不盡!