您的位置:首页 > 其它

红黑树—Red Black Tree

2015-03-28 21:47 411 查看
红黑树是具有以下五条性质的二叉查找树:

每个结点要么是红的要么是黑的。
根结点是黑的。
每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。
如果一个结点是红的,那么它的两个儿子都是黑的。
对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点。



正是由于红黑树的以上五个性质,使得其高度最多是2log(N+1),从而保证红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。具体关于红黑树的基本介绍,以及插入操作,和删除操作的算法的具体分析可参考博文:教你透彻了解黑树(其结构之法、算法之道系列文章整体不错,可以多多参考学习)。
在插入、删除过程中最关键的就是时刻保证RBT的五个性质。

以下主要给出RBT的插入算法的具体实现,删除操作目前还没彻底弄清楚,后续再补充。其中的左旋转,右旋转,以及双旋转操作与平衡二叉树中的操作一样,

具体见:平衡二叉树(AVLTree)

插入操作:首先按查找二叉树的方法找到正确的插入位置,并将新节点X设为红色。则主要分为以下4中情况:

1、其父节点P为黑色,则直接插入,完成。

2、其父节点P为红色(则祖父节点GP一定为黑色),X为P(GP的左节点,为右节点时,对称情况)的左子节点(为右节点时,对称情况),其叔父节点S(即 GP的 右节点)为黑色,则执行一次左单旋转即可。 (对称情况执行一次右单旋转)。

3、其父节点P为红色(则祖父节点GP一定为黑色),X为P(GP的左节点,为右节点时,对称情况)的右子节点(为左节点时,对称情况),其叔父节点S(即 GP的
右节点)为黑色,则执行一次双旋转即可。 (对称情况类似执行一次双旋转)。

4、其父节点P为红色(则祖父节点GP一定为黑色),其叔父节点S为红色。 这种情况相对较为复杂。基本思路是,在插入新节点的自顶向下遍历过程中,将遇到的所有
双红儿子节点(其父节点一定为黑)的情况进行颜色翻转,即将父节点设为红,两儿子改为黑,从而将第4种情况转化为2或3的情况。

具体代码如下:

<span style="font-size:18px;">typedef enum ColorType {Red,Black} ColorType;
typedef int ElementType;
typedef struct RedBlackNode* RedBlackTree;
typedef struct RedBlackTree Position;

//节点
struct RedBlackNode
{
ElementType Element;//节点value值
RedBlackTree Left;//节点左指针
RedBlackTree Right;//节点右指针
ColorType Color;//节点颜色

};

/*旋转处理函数
根据需要进行单旋转(双旋转由两次单旋转完成)
参数:
Item:新节点value值
Parent:插入新节点后的曾祖父节点
返回值:
旋转后的新树根(新节点的祖父节点)指针
*/
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?
SingleRotateWithRight(Parent->Right)://“一”形
SingleRotateWithLeft(Parent->Right);//“之”形
}

static Position X,P,GP,GGP;

/*树形调整函数*/
static void HandleRrorient(ElementType Item,RedBlackTree T)
{
//在自顶向下遍历时遇到双子节点均为红时,执行颜色翻转
X->Color=Red;
X->Left->Color=Black;
X->Right->Color=Black;

if(P->Color==Red)//当父节点需要执行旋转操作
{
GP->Color=Red;
if(Item<P->Element != P->Element<GP->Element)//为”之“形,需要执行双旋转
P=Rotate(Item,GP);//对X和P执行一次单旋转(双旋转的第一次单旋转)
X=Rotate(Item,GGP);//为”一“形,旋转后X为新子树的树根
X->Color=Black;//确保树根为黑色
}

}

RedBlackTree Insert(ElementType Item ,RedBlackTree T)
{
X=P=GP=GGP=T;
/*Position NullNode=malloc(sizeof(struct RedBlackNode));
NullNode->Left=Right=NULL;
NullNode->Color=Red;
*/
NullNode->Element=Item;
while(X->Element!=Item && 	X!=NullNode)//自定向下遍历,并处理双红儿子节点,以及带来的旋转操作
{
GGP=GP;GP=P;P=X;
if(X<X->Element)
X=X->Left;
else
X=X->Right;
if(X->Left->Color==Red && X->Right->Color==Red)//双红儿子节点
HandleRrorient(Item,T);
}

if(X!=NullNode)//原来一存在该值的节点
return NullNode;

X=malloc(sizeof(struct RedBlackNode));
if(X=NULL)
printf("Out of space \n");
X->Element=Item;
X->Left=X->Right=NullNode;

//将新节点插入到树叶
if(Item<P->Element)
P->Left=X;
else
P->Right=X;
//最后插入节点后可能使得不满足红黑树的性质,因此需要最后一次调整(因为已不存在其叔父节点为红的情况,因此只需要进行一次单或者双旋转即可)
HandleRrorient(Item,T);

return T;
}
</span>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: