二叉搜索树和红黑树

二叉搜索树的结构:

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

若有错误请多担待,谢谢!

posted @ 2018-11-29 17:09  M-Anonymous  阅读(3165)  评论(0编辑  收藏  举报