1. 程式人生 > >紅黑樹插入和刪除原理

紅黑樹插入和刪除原理

紅黑樹本質是一顆二叉查詢樹,增加了著色以及相關的性質,使得紅黑樹的查詢,插入,刪除的時間複雜度最壞為O(log n)。

一、紅黑樹相對二叉查詢樹來說,有以下五個性質。

a.紅黑樹的節點不是紅色就是黑色

b.紅黑樹中根節點必是黑色。

c.紅黑樹上的節點時紅色,它的兩個子節點必須是黑色

d.樹中任意一個節點到葉子結點的路徑上的黑色節點數目相同

f.每個葉子節點都是黑色

二、理解左旋與右旋

   當對紅黑樹進行刪除或者是插入工作時,為了保證樹的平衡與性質,必須要對紅黑樹進行調整,調整時可能需要進行旋轉操作。通過對節點的著色以及樹的旋轉操作,來對紅黑樹進行插入或者是刪除節點等操作。當父節點顏色為紅色,需要通過旋轉或者是顏色改變來維護紅黑樹的性質。

1.左旋,以下是左旋轉的C++原始碼(來源於STL原始碼剖析)


inline void _rb_tree_rotate_left(_rb_tree_node_base*x,_rb_tree_node_base*&root)

{    

x為旋轉點

    _rb_tree_node_base*y=x->right;//令y為旋轉點的右節點

    x->right=y->left;

    if(y->left!=0)

    {

       y->left->parent=x;//設定父節點

    }

    //令y完全頂替x的地位

    y->parent=x->parent;

    if(x==root)    //如果x為根節點

        root=y;

    else if(x==x->parent->left)//如果x為其父節點的左節點

        x->parent->left=y;

    else                               //如果x為父節點的右節點

        x->parent->left=y;

        y->left=x;

        x->parent=y;

}

2.右旋,以下是右旋轉的C++原始碼(來源於STL原始碼剖析)


inline void _rb_tree_rotate_left(_rb_tree_node_base*x,_rb_tree_node_base*&root)

{

    x為旋轉點

    _rb_tree_node_base*y=x->left;//令y為旋轉點的右節點

    x->left=y->right;

    if(y->right!=0)

    {

       y->right->parent=x;//設定父節點

    }

    //令y完全頂替x的地位

    y->parent=x->parent;

    if(x==root)    //如果x為根節點

        root=y;

    else if(x==x->parent->lright)//如果x為其父節點的左節點

        x->parent->right=y;

    else                               //如果x為父節點的右節點

        x->parent->left=y;

        y->right=x;

        x->parent=y;

}

如果用到雙旋轉,只是左旋和右旋轉的結合應用,這裡不一一細描述。樹在經過左旋右旋之後,樹的搜尋性質保持不變,但樹的紅黑性質則被破壞了,所以,紅黑樹插入和刪除資料後,需要利用旋轉與顏色重塗來重新恢復樹的紅黑性質。

三、紅黑樹的插入節點

通過紅黑樹的性質的分析,插入節點會產生四種不同的典型情況,分析以下圖RB-Tree分別插入3,8,35,75根據二叉查詢樹的規則,我們分別得到以下圖的插入位置,我們發現它們都破壞了紅黑樹的規則,我們必須調整樹形,也就是旋轉樹形以及改變節點顏色。(以下灰色為紅色,深色為黑色)。


以上1.2.3.4為四種不符合紅黑樹的四種情況,破外了紅黑樹的性質,我們必須通過旋轉樹或者是調整節點顏色。為了方便定義,讓我們首先為了這些特殊節點定義一些代名,以下討論將沿用這些代名。假設新節點為X,其父節點為P,祖父節點為G,伯父節點(父親之兄弟節點)為S,曾祖父節點為GG,現在根據二叉查詢樹的規則,新節點必然是葉節點。根據紅黑樹規則4,X必為紅色。若P為紅色(這就違反了規則3,必須調整樹形)則G為黑色(原為RB-tree,必須遵循規則3)。於是,根據X的插入位置以及外圍的節點(S和GG)的顏色,有四種以下典型情況。

1.S為黑且X為外側插入。對此情況,我們隊P,G做了一次但旋轉,並更改P,G顏色,即可重新滿足紅黑樹的規則3。見下圖


注意,此時可能產生不平衡狀態(高度相差為1以上)。例如圖中A和B為null,D或者E不為NULL。這倒是沒關係,因為RB-Tree的平衡性本來就比AVL-tree弱。然而RB-tree通常能夠導致良好的平衡狀態。是的,經驗告訴我們,RB-tree的搜尋平均效率和AVL-tree幾乎相等。

2.S為黑且X為內側插入。對此情況,我們必須先對P,X做一次單旋轉並更改G,X顏色,再將結果對G做一次單旋轉,即可在此滿足紅黑樹規則3.


3.S為紅色X為外側插入。對此情況。我們先對P和G做一次單旋轉,並改變X的顏色。此時如果GG為黑,一切搞定,但如果GG為紅色,則問題如4狀況。


4.S為紅色X為外側插入,對此情況,先對P和G做一次單旋轉,並改變X的顏色。此時如果GG亦為紅色,還要持續往上做,直到不再有父子連續併為紅色的情況。

 

四、紅黑樹的刪除

紅黑樹是一種特殊的二叉查詢樹,其刪除結點首先要按二叉查詢樹刪除結點的演算法進行

(一).普通二叉查詢樹刪除一個結點:

(1)待刪除結點沒有子結點,即它是一個葉子結點,此時直接刪除

(2)待刪除結點只有一個子結點,則可以直接刪除;如果待刪除結點是根結點,則它的子結點變為根結點;如果待刪除結點不是根結點,則用它的子結點替代它的位置。

(3)待刪除結點有兩個子結點,首先找出該結點的後繼結點(即右子樹中數值最小的那個結點),然後將兩個結點進行值交換(即:只交換兩個結點的數值,不改變結點的顏色)並將待刪除結點刪除,由於後繼結點不可能有左子結點,對調後的待刪除結點也不會有左子結點,因此要把它刪除的操作會落入情況(1)或情況(2)中。

(二)紅黑樹的刪除結點演算法

1.待刪除結點有兩個外部結點,操作如下:

(1)直接把該結點調整為葉結點

(2)若該結點是紅色,則可直接刪除,不影響紅黑樹的性質,演算法結束  

(3)若該結點是黑色,則刪除後紅黑樹不平衡。此時要進行“雙黑”操作         

 記該結點為V,則刪除了V後,從根結點到V的所有子孫葉結點的路徑將會比樹中其他的從根結點到葉結點的路徑擁有更少的黑色結點,破壞了紅黑樹性質4。此時,用“雙黑”結點來表示從根結點到這個“雙黑”結點的所有子孫葉結點的路徑上都缺少一個黑色結點。  
雙黑含義:該結點需要代表兩個黑色結點,才能維持樹的平衡


2.待刪除結點有一個外部結點

操作為:該節點是黑色,其非空子節點為紅色 ;則將其子節點提升到該結點位置,顏色變黑

3.“雙黑”結點的處理

分三種情況:

(1)雙黑色結點的兄弟結點是黑色,且子結點有紅色結點

(2)雙黑結點的兄弟結點是黑色,且有兩個黑色結點

(3)雙黑結點的兄弟結點是紅色結點

(1)雙黑結點的兄弟結點是黑色,且子結點有紅色結點

A種情況:雙黑結點遠侄子結點(雙黑結點若為左孩子,則雙黑結點的兄弟結點的右孩子為遠侄子結點;同理,處理雙黑結點為右孩子)為紅色,如圖2

處理方法:把兄弟結點染為雙黑結點的父親結點的顏色,把兄弟結點的右孩子染為黑色,再把父結點染為黑色;然後針對父結點進行一次左旋轉,如圖3 。


B種情況:雙黑結點近侄子結點(雙黑結點若為左孩子,則雙黑結點的兄弟結點的左孩子為近侄子結點;同理,處理雙黑結點為右孩子)為紅,如圖4

處理方法:針對雙黑結點的兄弟做一次右旋轉,結果使雙黑結點的近侄子成為雙黑結點新的兄弟;將新兄弟結點著為雙黑結點的父結點的顏色,父結點著為黑色,再針對父做一次左旋轉,如圖5


(2)雙黑結點的兄弟結點是黑色,且有兩個黑色結點,如圖6

處理方法:把雙黑結點的兄弟結點著為紅色,雙黑結點的父結點著為黑色;若父結點原來為紅色,則演算法結束;若父結點原來黑色,則將父結點作為雙黑結點,繼續調整,如圖7


(3)雙黑結點的兄弟結點是紅色結點,如圖8

處理方法:單旋轉為情況1或情況2,並改變雙黑結點的兄弟結點的顏色及父結點的顏色(如圖9)


文章參考了侯捷的《STL原始碼剖析》以及參考了 http://gengning938.blog.163.com/blog/static/1282253812011420103852696/