• 二叉搜索树和红黑树


    二叉搜索树的结构:

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

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

  • 相关阅读:
    0719PHP基础:PDO
    0717PHP基础:面向对象
    0716PHP基础:面向对象
    0715JS基础:ajax
    0715PHP练习:文件操作
    0715PHP基础:文件操作
    0629正则表达式:基础
    0628正则表达式:练习
    zTree简单使用和代码结构
    servlet
  • 原文地址:https://www.cnblogs.com/M-Anonymous/p/9926354.html
Copyright © 2020-2023  润新知