紅黑樹

1. 概念

  紅黑樹是一棵二叉查找樹,它在每一個結點上增長了一個存儲爲來表示結點的顏色,能夠RED或BLACK。經過對任意一條從根到葉子的簡單路徑上各個結點的顏色進行約束,紅黑樹確保沒有一條路徑會比其餘路徑長出2倍,於是是近似於平衡。node

  樹中每一個結點都包含5個屬性:color、key、left、right和p。若是一個結點沒有子結點或者父結點,則該結點相應指針屬性值爲NIL,視爲外結點同樣的葉子結點。c++

2. 性質

每一棵紅黑樹都須要知足下面的性質:指針

  • 每一個結點或是紅色的,或是黑色的
  • 根結點是黑色的
  • 每一個葉結點(NIL)是黑色的
  • 若是一個結點是紅色的,則它的兩個子結點都是黑色的
  • 對每一個結點,從該結點到其全部後代葉子結點的簡單路徑上,均包含相同數目的黑色結點

image_1bdjjp3qlukk1avknvcn1os09.png-70.3kB

  爲了方便和節省空間,常常在紅黑樹T中設置一個屬性T.nil,而後全部結點的空指針指向其:code

image_1bdjjv4lldeg6h5q5kf4915dcm.png-83.7kB

  從某個結點x出發(不含該結點)到達一個葉結點的任意一條簡單路徑上的黑色結點個數稱爲該結點的黑高,記爲bh(x)。blog

一棵有n個內部結點的紅黑樹的高度至多爲2lg(n+1),這保證了其查找操做都在O(lgn)內能完成class

3. 旋轉

  在插入和刪除操做後,紅黑樹的性質可能會被破壞,這時候就要經過旋轉來進行調整。旋轉分爲2種:左旋和右旋。當在某個結點x上左左旋時,x右孩子不爲T.nil結點,則左旋後,x成爲y的左孩子,y的左孩子成爲x的右孩子。右旋同樣的道理。原理

  一棵紅黑樹包括兩個結點:bfc

struct RBT {
        node * root;
        node * nil;
    };

image_1bdjl6taingonda1l7e1sjl1ba713.png-35.6kB

  • 代碼以下:
LEFT_ROTATE(T,x)
    {
        y = x.right;
        x.right = y.left;
        if y.left != T.nil
            y.left.p = x;
        y.p = x.p;
        if x.p == T.nil
            T.root = y;
        elsif x == x.p.left
            x.p.left = y;
        else
            x.p.right = y;
            
        y.left = x;
        x.p = y;
    }
    
    //RIGHT_ROTATE相似

4. 插入

  講道理插入原理和二叉查找樹差很少,只不過多了一個維護性質的過程。二叉樹

  • 代碼以下
RB_INSERT(T,z) 
    {
        y = T.nil;
        x = T.root;
        while x != T.nil {
            y = x;
            if z.key < x.key
                x = x.left;
            else
                x = x.right;
        }
        z.p = y;
        if y == T.nil
            T.root = z;
        elsif z.key < y.left
            y.left = z;
        else
            y.right = z;
        
        z.left = T.nil;
        z.right = T.nil;
        z.color  = RED;
        RB_INSERT_FIXUP(T,z);
    }
    
    
    RB_INSERT_FIXUP(T,z)
    {
        while z.p.color == RED {
            if z.p == x.p.p.left {
                y = z.p.p.right;
                if y.color == RED {     //case 1
                    z.p.color = BLACK;
                    y.color = BLACK;
                    z.p.p.color = RED;
                    z = z.p.p;
                } elsif z == z.p.right {   //case 2
                    z = z.p;
                    LEFT_ROTATE(T,z);
                }
                z.p.color = BLACK;    //case 3
                z.p.p.color = RED;
                RIGHT_ROTATE(T,z.p.p);
            } else 
                //直接是上個代碼left<->right調換便可
        }
        
        T.root.color = BLACK;
    }

  其實有6種狀況能夠改變紅黑樹的性質,可是因爲是對稱的,因此只給了一邊的代碼,具體狀況圖示:im

image_1bdjnut2u31s1j33bilnm112dd1g.png-88.2kB

5. 刪除

  刪除其實和查找二叉樹的刪除相似,只是多了一個維護性質的過程,先貼出代碼:

//v爲根的子樹替換u爲根的子樹
    RB_TRANSPLANT(T,u,v)
    {
        if u.p == T.nil
            T.root = v;
        elsif u == u.p.left
            u.p.left = v;
        else
            u.p.right = v;
        v.p = u.p;
    }
    
    RB_DELETE(T,z)
    {
        y = z;
        y_original_color = y.color;
        if z.left == T.nil
            x = z.right;
            RB_TRANSPLANT(T,z,z.right);
        elsif z.right == T.nil
            x = z.left;
            RB_TRANSPLANT(T,z,z.left);
        else
            y = TREE_MINIMUM(z.right);
            y_original_color = y.color;
            x = y.right;
            if y.p == z
                x.p = y;
            else
                RB_TRANSPLANT(T,y,y.right);
                y.right = z.right;
                y.right.p = y;
                
            RB_TRANSPLANT(T,z,y);
            y.left = z.left;
            y.left.p = y;
            y.color = z.color;
            
        //只有顏色爲黑纔會破壞性質
        if y_original_color == BLACK
            RE_DELETE_FIXUP(T,x);
    }
    
    
    RB_DELETE_FIXUP(T,x)
    {
        while x != T.root and x.color == BLACK
        {
            if x== z.p.left
                w = x.p.right;
                if w.color == RED    //case 1
                    w.color = BLACK;
                    x.p.color = RED;
                    LEFT_ROTATE(T,x.p);
                    w = x.p.right;
                if w.left.color == BLACK and w.right.color == BLACK //case 2
                    w.color = RED;
                    x = x.p;
                eleif w.right.color == BLACK   //case 3
                    w.left.color = BLACK;
                    w.color = RED;
                    RIGHT_ROTATE(T,w);
                    w = x.p.right;
                w.color = x.p.right;  // case 4
                x.p.color = BLACK;
                w.right.color = BLACK;
                LEFT_ROTATE(T,x.p);
                x = T.root;
            else
                //和上面同樣,將left互換right 
        }           
    }

有8種狀況,可是是對稱的,因此只給出4種狀況的代碼,具體看圖:

image_1bdjr3i6u2tsucbfnk1pi71d201t.png-169.6kB

相關文章
相關標籤/搜索