二叉搜索樹和紅黑樹


二叉搜索樹的結構:

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

二叉搜索樹的性質:

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

如果 y 是 x 右子樹中的一個節點,那么 y.data >= x.data。

不同的二叉搜索樹可以代表同一組值的集合。

插入代碼:

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;
}

 

刪除操作:

刪除操作共有如下四種情況:

右下角的那種情況 Min 結點是 R子樹中值最小的一個結點,所以它的左孩子為空。

 

刪除代碼:

1、替換函數:將結點 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;
}

2、結點中的最小值。

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

3、刪除結點 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;

 

紅黑性質:

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

2、根節點是黑色的。

3、每個葉結點是黑色的。

4、如果一個結點是紅色的,則它的兩個子結點都是黑色的。

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

 

黑高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;
}

 

插入操作:

在進行編寫代碼之前,需要分析一下所有的插入情況:

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

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

      這時將不滿足性質 4。但可以作相應調整:

 

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

      

三、插入結點 A 的父結點 B 為紅色,但是 B 結點的兄弟為黑色,

      這是也不滿足性質 4,也可以作出相應調整:

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

       而圖三、圖四分別左旋 BA,右旋 BA 后,就變成了圖一、圖二。

       相關操作如下所示:

      

 

插入代碼:

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) 時間。

下面給出幾種刪除情況:

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

      這幾種情況可以直接將結點 A 為刪除,紅黑樹性質不會被破壞。

      刪除結點 A 后情況如下圖:

二、比較復雜的就是如下左邊的這種圖,因為此時會破壞紅黑性質 5 或可能破壞紅黑性質 4。

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

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

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

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

      因為要保持紅黑性質,所以有如下兩種情況:

    

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

 

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

      因為當移走黑色的結點 Min 后,會破壞紅黑性質 5,可能會破壞紅黑性質 4。

   1、當 Min 結點的右孩子 C 為紅色時的情況如下:

     

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

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

    2、當 Min 結點的右孩子為黑色時的情況如下:

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

        所以當 P 的顏色為紅色時,我們只需要左旋一下 PB,並將 P結點顏色變成黑色即可。

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

        因此我們把這兩種情況和成一種情況,就是把 P 的顏色當作黑色討論。

        3.1、對於前三個圖,我們可以歸為一種情況:也就是第二個圖的那種情況:

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

             我們先將 PB 左旋,然后顏色互換,再將 C 結點的顏色變成黑色即可。

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

         

         3.2、對於第五個圖其實可以和第四個圖同為一種情況。

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

              所以此時我們得在 P 樹的祖先中尋找一個紅色結點來增加 P 樹的樹高。

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

              當 P 作為新的起始點后,我們需要判斷 P 結點是其父結點的左孩子還是右孩子。

              如果是左孩子則執行相同的操作,否則便將該左旋的地方右旋,該右旋的地方左旋,

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

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

刪除代碼:

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

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;
}

2、紅黑樹刪除:

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);
}

3、紅黑樹修正:

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;
}

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM