您的位置:首页 > 编程语言

红黑树增删操作的代码实现(上)

2013-07-24 14:58 204 查看
这几天一直在学习红黑树,由于是第一次接触,所以刚开始觉得挺麻烦的,经过这几天的各种google,终于对其的插入和删除操作有了一定了解,现在就分享下:

红黑树的定义:红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:

性质1. 节点是红色或黑色。

性质2. 根是黑色。

性质3. 所有叶子都是黑色(叶子是NIL节点)。

性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点。

性质5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。

首先是红黑树的结构定义:红黑树定义为一个拥有5个属性的结构体:

typedef struct RBTNode{
int color;
struct RBTNode *parent;
struct RBTNode *left;
struct RBTNode *right;
int value;
}RBTNode,*RBTree;


在实现红黑树的插入操作时,首先要实现树的旋转操作,这在插入删除操作之后调整红黑树时要用到,分为左旋和右旋操作,实现代码如下:

//右旋操作实现代码
void RIGHT_ROTATE(RBTree T,RBTree x)
{
RBTree y;
y = x->left;
x->left = y->right;
if(y->right!=nil)
{
y->right->parent = x;
}
y->parent = x->parent;
if(x->parent == nil)
{
root = y;
}
else if(x->parent->left == x)
{
x->parent->left = y;
}
else
{
x->parent->right = y;
}
y->right = x;
x->parent = y;
}

//左旋操作实现代码,与右旋操作一样
void LEFT_ROTATE(RBTree T,RBTree x)
{
RBTree y;
y = x->right;
x->right =y->left;
if(y->left!=nil)
{
y->left->parent = x;
}
y->parent = x->parent;
if(x->parent == nil)
{
root = y;
}
else if(x == x->parent->left)
{
x->parent->left = y;
}
else
{
x->parent->right = y;
}
y->left = x;
x->parent = y;
}
左旋和右旋操作原理比较简单,看代码就能理解。

之后是红黑树的插入操作,插入操作其实和普通的二叉排序树的插入操作一样,只是在插入结点之后,红黑树的5条性质可能会被破坏,这样就需要调用调整函数对红黑树进行调整,以让其满足红黑树的5条性质,

红黑树的插入实现函数如下:
//插入操作,这与一般的二叉排序树的插入操作类似
void RBTreeInsert(RBTree T,RBTree z)
{
RBTree x,y;
y = nil;
x = root;
while(x!=nil)
{
y = x;
if(z->value<x->value)
{
x = x->left;
}
else
{
x = x->right;
}
}
z->parent = y;
if(y == nil)
{
root = z;
}
else if(z->value<y->value)
{
y->left = z;
}
else
{
y->right = z;
}
z->left = nil;
z->right = nil;
z->color = RED;
RBTreeInsertFixup(T,z);

}
在将结点插入到红黑树的相应位置之后,将新结点涂红(如果涂黑,就会导致根到叶子的路径上有一条路上,多了一个额外的黑结点,这个是很难调整的。但涂红后可能会导致出现两个连续的红色结点的冲突,那么可以通过颜色调整和树旋转来调整)。

插入新结点之后,会出现如下需要调整红黑树的情况(还有几种情况没列出,因为那些情况不需要调整树)

1.当红黑树中没有结点时,新结点直接涂黑就可以了。



2.新结点的父亲结点为红色,并且新结点的叔叔结点也为红色



调整方法:父=>黑;叔=>黑;祖=>红;往上遍历

3.新结点的父结点为红色,叔叔结点为黑色,新结点为其父结点的左孩子



调整方法:父=>黑;祖=>红;祖父右旋转;

4.新结点的父结点为红色,叔叔结点为黑色,新结点为其父结点的右孩子



调整方法:新=>黑;祖=>红;父左旋转;祖右旋转

具体的实现代码如下所示:

void RBTreeInsertFixup(RBTree T,RBTree z)
{
RBTree y;
while(z->parent->color == RED)//这时说明z肯定存在父结点和祖父结点
{
if(z->parent == z->parent->parent->left)//如果z的父结点为祖父结点的左孩子
{
y = z->parent->parent->right;//y为z的叔叔结点
if(y->color == RED)//情况1:红叔情况
{//将父结点和叔叔结点涂黑,祖父结点涂红
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;//调整之后将祖父结点作为调整对象
}
else
{
if(z == z->parent->right)//z是父结点的右孩子
{//情况2:黑叔,新结点z为右孩子
z = z->parent;
LEFT_ROTATE(T,z);
}
//通过上面两部操作将情况2转为情况3,然后两种情况一起操作
//情况3:黑叔,z为右孩子
z->parent->color = BLACK;
z->parent->parent->color = RED;
RIGHT_ROTATE(T,z->parent->parent);
}

}
else//如果父结点为祖父结点的右孩子
{
y = z->parent->parent->left;//y为z的叔叔结点
if(y->color == RED)//情况1:红叔
{//将父结点和叔叔结点涂黑,祖父结点涂红
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;//调整之后将祖父结点作为调整对象
}
else
{
if(z == z->parent->left)//z为父结点的左孩子
{//情况2:黑叔,新结点为左孩子
z = z->parent;
RIGHT_ROTATE(T,z);
}
//情况3:黑叔,新结点为右孩子
z->parent->color = BLACK;
z->parent->parent->color = RED;
LEFT_ROTATE(T,z->parent->parent);
}
}
}
root->color = BLACK;//包含情况:z为唯一的结点的情况
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: