二叉搜索樹和紅黑樹

二叉搜索樹的結構:算法

typedef int ElemType;
typedef struct SearchBiTree
{
    ElemType Data;
    struct SearchBiTree *LChild,*RChild,*Parent;
}SearchBiTree,*PSearchBiTree;

二叉搜索樹的性質:函數

設 x 是二叉搜索樹中的一個節點。若是 y 是 x 左子樹中的一個節點,那麼 y.data <= x.data。spa

若是 y 是 x 右子樹中的一個節點,那麼 y.data >= x.data。3d

不一樣的二叉搜索樹能夠表明同一組值的集合。code

插入代碼:blog

void Tree_Insert(PSearchBiTree &T,PSearchBiTree z)
{
    PSearchBiTree y = NULL;
    PSearchBiTree x = T;
    while(x != NULL)
    {
        y = x;
        if(z->Data < x->Data)
            x = x->LChild;
        else
            x = x->RChild;
    }
    z->Parent = y;
    if(y == NULL)
        T = z;
    else if(z->Data < y->Data)
        y->LChild = z;
    else
        y->RChild = z;
}

 

刪除操做:class

刪除操做共有以下四種狀況:搜索

右下角的那種狀況 Min 結點是 R子樹中值最小的一個結點,因此它的左孩子爲空。im

 

刪除代碼:db

一、替換函數:將結點 v 替換 T 樹中的結點 u。

void Transplant(PSearchBiTree &T,PSearchBiTree u,PSearchBiTree v)
{
    if(u->Parent == NULL)
        T = v;
    else if(u == u->Parent->LChild)
        u->Parent->LChild = v;
    else
        u->Parent->RChild = v;
    if(v != NULL)
        v->Parent = u->Parent;
}

二、結點中的最小值。

PSearchBiTree Tree_Minimum(PSearchBiTree T)
{
    while(T->LChild != NULL)
        T = T->LChild;
    return T;
}

三、刪除結點 z。

void Tree_Delete(PSearchBiTree &T,PSearchBiTree z)
{
    PSearchBiTree y = NULL;
    if(z->LChild == NULL)
        Transplant(T,z,z->RChild);
    else if(z->RChild == NULL)
        Transplant(T,z,z->LChild);
    else
    {
        y = Tree_Minimum(z->RChild);
        if(y->Parent == z)
        {
            Transplant(T,y,y->RChild);
            y->RChild = z->RChild;
            y->RChild->Parent = y;
        }
        Transplant(T,z,y);
        y->LChild = z->LChild;
        y->LChild->Parent = y;
}
}

 

紅黑樹:

算法導論中樹的高度彷佛並不算樹根。

紅黑樹是許多"平衡"搜索樹中的一種,能夠保證在最壞狀況下基本動態集合操做的時間複雜度爲O(lgn)。

紅黑樹是一顆二叉搜索樹,它相對二叉搜索樹增長了一個存儲位來標識結點顏色,可使 Red 或 Black。

經過對任何一條從根到葉子的簡單路徑上各個結點的顏色進行約束,確保沒有一條路徑會比其餘路徑長出兩倍。

咱們一般把帶關鍵字的結點稱爲內部結點,不帶關鍵字的結點而且其沒有子結點或父結點的結點稱爲外部結點

 

紅黑樹結構:

typedef enum {Red,Black}RB_Color;
typedef struct RBTree
{
    ElemType Data;
    struct RBTree *Left,*Right,*Parent;
    RB_Color Color;
}RBTree,*PRBTree;

 

紅黑性質:

一、每一個結點或是紅色的,或是黑色的。

二、根節點是黑色的。

三、每一個葉結點是黑色的。

四、若是一個結點是紅色的,則它的兩個子結點都是黑色的。

五、對每個結點,從該結點到其後代葉結點的簡單路徑上,均包含相同數目的黑色結點。

 

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

引理:一顆有 n 個內部結點的紅黑樹的高度至多爲 2lg(n+1)。

推論:一顆高度爲 h 的紅黑樹,黑高bh 至少爲 (h/2)向上取整,最多爲 h。

           至少有 2^bh - 1 個結點,最多有 4^bh - 1個結點。

 

旋轉操做:

如圖,從右到左爲左旋,從左到右爲右旋。

旋轉代碼:

void Left_Rotate(PRBTree &T,PRBTree x)
{
    PRBTree y = x->Right;
    x->Right = y->Left;
    if(y->Left != NULL)
        y->Left->Parent = x;
    y->Parent = x->Parent;
    if(x->Parent == NULL)
        T = y;
    else if(x == x->Parent->Left)
        x->Parent->Left = y;
    else
        x->Parent->Right = y;
    y->Left = x;
    x->Parent = y;
}

void Right_Rotate(PRBTree &T,PRBTree y)
{
    PRBTree x = y->Left;
    y->Left = x->Right;
    if(x->Right != NULL)
        x->Right->Parent = y;
    x->Parent = y->Parent;
    if(y->Parent == NULL)
        T = x;
    else if(y == y->Parent->Left)
        y->Parent->Left = x;
    else
        y->Parent->Right = x;
    x->Right = y;
    y->Parent = x;
}

 

插入操做:

在進行編寫代碼以前,須要分析一下全部的插入狀況:

1、插入結點 A 的父結點 B 爲黑色,此時插入不會破壞紅黑樹的性質。

2、插入結點 A 的父結點 B 爲紅色,且 B 結點的兄弟也爲紅色,

      這時將不知足性質 4。但能夠做相應調整:

 

      此時,將 B 結點以及 C 結點 變成黑色,將 D 結點變成紅色便可。

      

3、插入結點 A 的父結點 B 爲紅色,可是 B 結點的兄弟爲黑色,

      這是也不知足性質 4,也能夠做出相應調整:

      分別對以上四圖變化後,對圖1、圖二先變色後,分別右旋 DB,左旋 DB。

       而圖3、圖四分別左旋 BA,右旋 BA 後,就變成了圖1、圖二。

       相關操做以下所示:

      

 

插入代碼:

void RB_Insert(PRBTree &T,PRBTree z)
{
    PRBTree y = NULL;
    PRBTree x = T;
    while(x != NULL)
    {
        y = x;
        if(z->Data < x->Data)
            x = x->Left;
        else
            x = x->Right;
    }
    z->Parent = y;
    if(y == NULL)
        T = z;
    else if(z->Data < y->Data)
        y->Left = z;
    else
        y->Right = z;
    z->Left = NULL;
    z->Right = NULL;
    z->Color = Red;
    RB_Insert_Fixup(T,z);
}

插入修正代碼:

void RB_Insert_Fixup(PRBTree &T,PRBTree z)
{
    PRBTree y = NULL;
    while(z->Parent->Color = Red)
    {
        if(z->Parent == z->Parent->Parent->Left)
        {
            y = z->Parent->Parent->Right;
            if(y->Color == Red)
            {
                z->Parent->Color = Black;
                y->Color = Black;
                z->Parent->Parent->Color = Red;
                z = z->Parent->Parent;
            }
            else if(z = z->Parent->Right)
            {
                z = z->Parent;
                Left_Rotate(T,z);
            }
            z->Parent->Color = Black;
            z->Parent->Parent->Color = Red;
            Right_Rotate(T,z->Parent->Parent);
        }
        else
        {
            y = z->Parent->Parent->Left;
            if(y->Color = Red)
            {
                z->Parent->Color = Black;
                y->Color = Black;
                z->Parent->Parent->Color = Red;
                z = z->Parent->Parent;
            }
            else if(z == z->Parent->Left)
            {
                z = z->Parent;
                Left_Rotate(T,z);
            }
            z->Parent->Color = Black;
            z->Parent->Parent->Color = Red;
            Left_Rotate(T,z->Parent->Parent);
        }
    }
    T->Color = Black;
}

 

刪除操做:

與 n 個結點的紅黑樹上的其餘的基本操做同樣,刪除一個結點須要花費 O(lgn) 時間。

下面給出幾種刪除狀況:

1、先給出簡單的刪除狀況:被刪除結點 A 爲 紅色,結點 A 的兄弟和孫子沒有畫出。

      這幾種狀況能夠直接將結點 A 爲刪除,紅黑樹性質不會被破壞。

      刪除結點 A 後狀況以下圖:

2、比較複雜的就是以下左邊的這種圖,由於此時會破壞紅黑性質 5 或可能破壞紅黑性質 4。

     讓咱們先來分析一下,A 的父結點和子孫結點顏色不肯定,用藍色表示。

      如若咱們刪除 A 結點,則須要尋找一個結點替代結點 A 的位置並變成結點 A 的顏色。

      咱們能夠尋找比 A 小且相鄰的結點,也就是 A 的右子樹中最小的一個結點,用 Min 表示。

      咱們任然不知道結點 Min 的顏色,這裏先分析簡單的,讓它以紅色表示。

      由於要保持紅黑性質,因此有以下兩種狀況:

    

      這兩種狀況只須要簡單的將 Min 結點替換到 A 的位置並將顏色變成 A 的顏色便可。

 

3、任然是上面左邊兩個圖,當結點 Min 的顏色是黑色時,狀況就比較複雜了。

      由於當移走黑色的結點 Min 後,會破壞紅黑性質 5,可能會破壞紅黑性質 4。

   一、當 Min 結點的右孩子 C 爲紅色時的狀況以下:

     

      這種狀況比較簡單,只須要將 C 結點替換到 Min 結點的位置並將顏色變成黑色便可解決問題。

      Min 的兄弟結點只畫了一種狀況,其餘狀況也同樣,但要保持紅黑性質。

    二、當 Min 結點的右孩子爲黑色時的狀況以下:

        當 Min 結點刪除後,咱們須要找到一個紅結點填到 Min 的那個路徑上,並將顏色變成黑色。

        因此當 P 的顏色爲紅色時,咱們只須要左旋一下 PB,並將 P結點顏色變成黑色便可。

        可是當 P 的顏色爲黑色時,咱們就得在 P 的右子樹中尋找一個紅結點了。

        所以咱們把這兩種狀況和成一種狀況,就是把 P 的顏色看成黑色討論。

        3.一、對於前三個圖,咱們能夠歸爲一種狀況:也就是第二個圖的那種狀況:

             第二張圖的特色是 Min 結點的兄弟的右孩子 C 爲 紅色:

             咱們先將 PB 左旋,而後顏色互換,再將 C 結點的顏色變成黑色便可。

             第三個圖是先將 DC 右旋,而後顏色互換,就變成了第二張圖的狀況。

         

         3.二、對於第五個圖其實能夠和第四個圖同爲一種狀況。

              咱們已經沒法在 P 樹的內部尋找到一個合適的紅色結點來替換 Min 的位置了。

              因此此時咱們得在 P 樹的祖先中尋找一個紅色結點來增長 P 樹的樹高。

              咱們將 P 結點設爲新的起始點,代替原來 Min 的右結點也就是空結點。

              當 P 做爲新的起始點後,咱們須要判斷 P 結點是其父結點的左孩子仍是右孩子。

              若是是左孩子則執行相同的操做,不然便將該左旋的地方右旋,該右旋的地方左旋,

              屬性爲 left 的地方變成 right,屬性爲 right 的地方變成 left。

              總而言之,就是左右互換就對了。最後將起始點顏色變成黑色。

刪除代碼:

一、紅黑樹替換和尋找最小值:

void RB_Transplant(PRBTree &T,PRBTree u,PRBTree v)
{
    if(u->Parent == NULL)
        T = v;
    else if(u == u->Parent->Left)
        u->Parent->Left = v;
    else
        u->Parent->Right = v;
    v->Parent = u->Parent;
}

PRBTree RBTree_Minimum(PRBTree T)
{
    while(T->Left != NULL)
        T = T->Left;
    return T;
}

二、紅黑樹刪除:

void RB_Delete(PRBTree &T,PRBTree z)
{
    PRBTree y = z;
    PRBTree x = NULL;
    RB_Color Original_Color = y->Color;
    if(z->Left = NULL)
    {
        x = z->Right;
        RB_Transplant(T,z,z->Right);
    }
    else if(z->Right == NULL)
    {
        x = z->Left;
        RB_Transplant(T,z,z->Left);
    }
    else
    {
        y = RBTree_Minimum(z->Right);
        Original_Color = y->Color;
        x = y->Right;
        if(y->Parent == z)
            x->Parent = y;
        else
        {
            RB_Transplant(T,y,y->Right);
            y->Right = z->Right;
            y->Right->Parent = y;
        }
        RB_Transplant(T,z,y);
        y->Left = z->Left;
        y->Left->Parent = y;
        y->Color = z->Color;
    }
    if(Original_Color == Black)
        RB_Delete_Fixup(T,x);
}

三、紅黑樹修正:

void RB_Delete_Fixup(PRBTree &T,PRBTree x)
{
    PRBTree w = NULL;
    while(x != T && x->Color == Black)
    {
        if(x == x->Parent->Left)
        {
            w = x->Parent->Right;
            if(w->Color == Red)
            {
                w->Color = Black;
                x->Parent->Color = Red;
                Left_Rotate(T,x->Parent);
                w = x->Parent->Right;
            }
            if(w->Left->Color == Black && w->Right->Color == Black)
            {
                w->Color = Red;
                x = x->Parent;
            }
            else
            {
                if(w->Right->Color == Black)
                {
                    w->Left->Color = Black;
                    w->Color = Red;
                    Right_Rotate(T,w);
                    w = x->Parent->Right;
                }
                w->Color = x->Parent->Color;
                x->Parent->Color = Black;
                w->Right->Color = Black;
                Left_Rotate(T,x->Parent);
                x = T;
            }
        }
        else
        {
            w = x->Parent->Left;
            if(w->Color == Red)
            {
                w->Color = Black;
                x->Parent->Color = Red;
                Right_Rotate(T,x->Parent);
                w = x->Parent->Left;
            }
            if(w->Right->Color == Black && w->Left->Color == Black)
            {
                w->Color = Red;
                x = x->Parent;
            }
            else
            {
                if(w->Left->Color == Black)
                {
                    w->Right->Color = Black;
                    w->Color = Red;
                    Left_Rotate(T,w);
                    w = x->Parent->Left;
                }
                w->Color = x->Parent->Color;
                x->Parent->Color = Black;
                w->Left->Color = Black;
                Right_Rotate(T,x->Parent);
                x = T;
            }
        }
    }
    x->Color = Black;
}

如有錯誤請多擔待,謝謝!

相關文章
相關標籤/搜索