数据结构之红黑树

来源:互联网 发布:aⅴ淘宝2016 编辑:程序博客网 时间:2024/06/16 04:09

一、红黑树性质:

红黑树是具有以下性质的二叉查找树(左小右大):

1.每一个节点或者是红色或者是黑色
2.根是黑色
3.如果一个节点是红的,那么它的子节点必须的黑的
4.从一个节点到一个NULL指针的每一条路径必须包含相同数目的黑色节点。

红黑树的高度最多是2log(N+1),平均红黑树的深度和AVL树一样,(所以查找时间一般接近最优)红黑树的优点是执行插入操作所需要的开销相对较低,在实践中发生旋转相对较少。
困难的操作是插入和删除,需要通过颜色的改变和树的旋转来满足以上4条性质

二、红黑树的插入:
分情况讨论:
1、如果插入节点的父节点是黑色的,那么插入操作很容易完成,直接进行插入。
2、如果插入节点的父节点是红色的,【因为性质4.所以我们插入的节点一定是红色的,那么这时,我们需要进行颜色翻转和位置旋转来恢复性质】
插入红色节点,由于性质3,我们必须进行颜色翻转和位置旋转来完成插入,保持红黑树的性质
     分两种情况:

           一字型:

             

           之字形:
                 

我们对以下红黑树进行插入:

              


但是当插入45时上述的方法不行了,经过旋转后有两个连续的红节点。我们需要新的方法

             

这时,我们想到将红色节点颜色与黑色节点颜色进行翻转。因此又分为两种情况:
1、插入节点的父节点的兄弟节点是黑色的,则利用上述一字型、之字形规则颜色翻转和位置旋转直接恢复
2、插入节点的父节点的兄弟节点是红色的(即一个节点右两个红儿子),则需要将拥有两个红色儿子的节点与父节点颜色翻转,然后再进行插入

               

应用上述规则,我们重新插入45。

a、我们需要先对拥有两个红儿子的节点进行颜色翻转,然后再进行插入。

           

b、经过翻转后,出现一字型情形,我们进行一字型旋转。
           

c、经过一字型旋转,现在就可以直接插入节点45
           

因此我们得到红黑树插入操作的步骤:

1、在从根节点向插入节点,有两个红色儿子的节点则需要进行颜色翻转(下滤)
2、翻转完后若不满足红黑树性质,则进行旋转操作。
3、插入


三、程序实现:

        #include "redblack.h"        #include <stdlib.h>        #include "fatal.h"        typedef enum ColorType { Red, Black } ColorType;        struct RedBlackNode        {            ElementType  Element;            RedBlackTree Left;            RedBlackTree Right;            ColorType    Color;        };        static Position NullNode = NULL;          RedBlackTree        Initialize( void )        {            RedBlackTree T;            if( NullNode == NULL )            {                NullNode = malloc( sizeof( struct RedBlackNode ) );                if( NullNode == NULL )                    FatalError( "Out of space!!!" );                NullNode->Left = NullNode->Right = NullNode;                NullNode->Color = Black;                NullNode->Element = 12345;            }            T = malloc( sizeof( struct RedBlackNode ) );            if( T == NULL )                FatalError( "Out of space!!!" );            T->Element = NegInfinity;            T->Left = T->Right = NullNode;            T->Color = Black;            return T;        }        void        Output( ElementType Element )        {            printf( "%d\n", Element );        }        static void        DoPrint( RedBlackTree T )        {            if( T != NullNode )            {                DoPrint( T->Left );                Output( T->Element );                DoPrint( T->Right );            }        }        void        PrintTree( RedBlackTree T )        {            DoPrint( T->Right );        }        static RedBlackTree        MakeEmptyRec( RedBlackTree T )        {            if( T != NullNode )            {                MakeEmptyRec( T->Left );                MakeEmptyRec( T->Right );                free( T );            }            return NullNode;        }        RedBlackTree        MakeEmpty( RedBlackTree T )        {            T->Right = MakeEmptyRec( T->Right );            return T;        }        Position        Find( ElementType X, RedBlackTree T )        {            if( T == NullNode )                return NullNode;            if( X < T->Element )                return Find( X, T->Left );            else            if( X > T->Element )                return Find( X, T->Right );            else                return T;        }        Position        FindMin( RedBlackTree T )        {            T = T->Right;//            while( T->Left != NullNode )                T = T->Left;            return T;        }        Position        FindMax( RedBlackTree T )        {            while( T->Right != NullNode )                T = T->Right;            return T;        }        static Position        SingleRotateWithLeft( Position K2 )        {            Position K1;            K1 = K2->Left;            K2->Left = K1->Right;            K1->Right = K2;            return K1;  /* New root */        }        static Position        SingleRotateWithRight( Position K1 )        {            Position K2;            K2 = K1->Right;            K1->Right = K2->Left;            K2->Left = K1;            return K2;  /* New root */        }        static Position        Rotate( ElementType Item, Position Parent )//旋转        {            if( Item < Parent->Element )//判断是左旋转还是右旋转                return Parent->Left = Item < Parent->Left->Element ?                    SingleRotateWithLeft( Parent->Left ) :                    SingleRotateWithRight( Parent->Left );            else //判断是左旋转还是右旋转                return Parent->Right = Item < Parent->Right->Element ?                    SingleRotateWithLeft( Parent->Right ) :                    SingleRotateWithRight( Parent->Right );        }        static Position X, P, GP, GGP;//静态全局变量,保存节点X,父节点,祖父节点,曾祖父节点        static        void HandleReorient( ElementType Item, RedBlackTree T )//主要进行颜色翻转,并调用旋转函数        {            X->Color = Red;        //翻转X与其儿子节点的颜色            X->Left->Color = Black;            X->Right->Color = Black;    //X翻转完后颜色是红色            if( P->Color == Red )  //如果父节点是红色的            {                GP->Color = Red;                if( (Item < GP->Element) != (Item < P->Element) )//判断是否需要多一次旋转(之字形双旋转)                    P = Rotate( Item, GP );                  X = Rotate( Item, GGP );                X->Color = Black;            }            T->Right->Color = Black;  /* Make root black */        }        RedBlackTree        Insert( ElementType Item, RedBlackTree T )        {            X = P = GP = T;            NullNode->Element = Item;//这样初始化,当X下滤到叶子节点时,下面的while循环就会跳出            while( X->Element != Item )  //进行下滤             {                GGP = GP; GP = P; P = X;                if( Item < X->Element )                    X = X->Left;                else                    X = X->Right;                if( X->Left->Color == Red && X->Right->Color == Red )//如果X有两个红儿子节点,就进行颜色翻转,位置旋转                    HandleReorient( Item, T );            }            if( X != NullNode )//说明没有下滤到叶子节点,上述while循环就跳出了,所以Item已经在树中了,不用插入,返回NULL                return NullNode;  //将Item进行插入            X = malloc( sizeof( struct RedBlackNode ) );            if( X == NULL )                FatalError( "Out of space!!!" );            X->Element = Item;//执行到这一步说明树中没有Item,X已经等于NullNode,X->Color=Black;            X->Left = X->Right = NullNode;            if( Item < P->Element )  //将Item插入合适的位置                P->Left = X;            else                P->Right = X;            HandleReorient( Item, T ); //因为之前X的Color为黑色,所以这里将颜色变为红色            return T;        }        RedBlackTree        Remove( ElementType Item, RedBlackTree T )        {            printf( "Remove is unimplemented\n" );            if( Item )                return T;            return T;        }        ElementType        Retrieve( Position P )        {            return P->Element;        }

完整程序代码:GitHub

红黑树的具体实现比较复杂,我们使用两个标记节点:一个是根,一个是NullNode,它的作用像是在伸展树中那样是指示一个Null指针。
根标记将存储关键字-oo和一个指向真正的根的右指针。

四、插入程序执行过程分析:

插入元素45:
X=P=GP=T=-10000
进入while循环

1、

GGP=GP=-10000; GP=P=-10000; P=X=-10000;

Item=45>-10000

X=X->Right=30;

X->Left->Color == Red && X->Right->Color == Red 不成立

2、                    

GGP=GP=-10000;GP=P=-10000;P=X=30;

Item=45>30

X=X->Right=70;

X->Left->Color == Red && X->Right->Color == Red 不成立

3、

GGP=GP=-10000;GP=P=30;P=X=70;

Item=45<70

X=X->Left=60;

X->Left->Color == Red && X->Right->Color == Red 不成立

4、

GGP=GP=30;GP=P=70;P=X=60;

Item=45<60

X=X->Left=50;

X->Left->Color == Red && X->Right->Color == Red 成立

   HandleReorient( 45, T );

   将X与其儿子节点的颜色翻转互换,X翻转完后颜色是红色。

   P=60颜色也是红色,所以需要进行旋转变色

   根据上面旋转示意图可知,不论单旋转还是双旋转,都需要将G的颜色变为红色。

   一字型,进行单旋转。

   X = Rotate( 45, 30 );

   因为

   Item > Parent->Element 

   45>30      

   Item < Parent->Right->Element  

   45<70

   所以 

   SingleRotateWithLeft( Parent->Right ) 

   SingleRotateWithLeft( 70 ) 

   K2=70

   K1=60

   K2->Left=65

   K1->Right=70

   因此Parent->Right=60

   Rotate( 45, 30 )返回60

X=60;

最后再将X的颜色变为黑色。

T->Right->Color = Black并确保根的颜色为黑色

5、 

GGP=GP=70;GP=P=60;P=X=60;

Item=45<60

X=X->Left=50;

X->Left->Color == Red && X->Right->Color == Red 不成立

6、

GGP=GP=60;GP=P=60;P=X=50;

Item=45<50

X=X->Left=40;

X->Left->Color == Red && X->Right->Color == Red 不成立

7、

GGP=GP=60;GP=P=50;P=X=40;

Item=45>40

X=X->Right=NullNode;

X->Left->Color == Red && X->Right->Color == Red 不成立

8、跳出循环

Item=45 > P->Element=40
P->Right=X=45
HandleReorient( 45, T )
将X颜色变为红色,X子节点的颜色变为黑色
P->Color为黑色
T->Right->Color也为黑色

0 0
原创粉丝点击