您的位置:首页 > 其它

红黑树的插入(算法导论)

2014-11-01 22:40 260 查看
红黑树是一棵二叉搜索树,它在每个节点增加了一个存储位来表示节点的颜色。一颗红黑树是满足下面红黑性质的二查搜索树:

1)每个节点或是红色的,或是黑色的

2)根节点是黑色的

3)每个叶节点(NIL)是黑色的

4)如果一个节点是红色的,则它的两个子节点都是黑色的

5)对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。

对一颗有n个内部节点的红黑树,其高度至多为2lg(n+1)。这样可以保证红黑树的查找的时间复杂度控制在O(lgn)。

对每一颗红黑树,我们假如所有的叶子节点都为空,并且都是黑色的。而任何非空节点都是内部节点,并且只能是红色或黑色。

1、红黑树的插入

在红黑树的插入中,默认插入的节点是红色的,因为假如插入的节点为黑色的,则会导致红黑树的黑高不等,若是红色的则不会出现此问题,但是这时有可能出现插入节点的父节点也是红色的,这样就破坏了红黑树的第四条性质。这时就需要对红黑树进行调整。

红黑树的插入总共六种情况,其中三种与另外三种对称。假如要插入的节点为z,则

在此我们主要介绍z的父节点是其祖父节点的左孩子的情况。这种情况下又有三种情况,分别为:

1)z的叔叔节点y是红色的。这时将z的父节点和z的叔叔节点y都修改成黑色的,z的祖父节点A原来一定是黑色的,这时将它修改为红色的,并将新的z节点指向z的祖父节点,然后继续向上进行调整。



图1.1 z的叔叔节点为红色的情况

2)z的叔叔节点是黑色的,并且z是一个右孩子节点。此时,首先将z指针指向其父节点,然后以此时z指向的节点为中心进行左旋,于是将此种情况转化成了Case 3的情形。即z节点是左孩子节点。



图1.2 z的叔叔节点为黑色并且z为右孩子

3)z的叔叔节点是黑色的,并且z是一个左孩子节点。此时首先将z的父节点修改为黑色,然后将其祖父节点修改为红色,然后以z的祖父节点为中心进行右旋。



图1.3 z的叔叔节点为黑色并且z为左孩子

在调整结束后,需要将根节点的颜色重新更改为黑色

关于红黑树的创建,插入以及调整的C代码如下:

#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
int key, color;					//color = 0:red, 1:black
struct Node *left, *right, *p;
}NODE, *TREE;

TREE rb_insert_fixup(TREE r, NODE *z)
{
TREE y;
y = NULL;
while(z->p!=NULL && z->p->color == 0)
{
if(z->p == z->p->p->left)	//z的父节点是其祖父节点的左孩子, 此种情形有三种情况
{
y = z->p->p->right;
if(y!=NULL && y->color == 0)	//case1: z的叔叔节点为红色
{
z->p->color = 1;
y->color = 1;
z->p->p->color = 0;
z = z->p->p;
}
else
{
if(z == z->p->right)	//case2: z的叔叔节点为黑色,并且z是右孩子 ,此时左旋后变成case3的情形
{
z = z->p;
r = left_rotate(r, z);
}
//case3: z的叔叔节点为黑色,并且z是左孩子
z->p->color = 1;
z->p->p->color = 0;
r = right_rotate(r, z->p->p);
}
}
else					//z的父节点是其祖父节点的右孩子,此种情形也有三种情况,并且与上面三种情况对称
{
y = z->p->p->left;
if(y->color == 0)
{
z->p->color = 1;
y->color = 1;
z->p->p->color = 0;
z = z->p->p;
}
else
{
if(z == z->p->left)
{
z = z->p;
r = right_rotate(r, z);
}
z->p->color = 1;
z->p->p->color = 0;
r = left_rotate(r, z->p->p);
}
}
}
r->color = 1;
return r;
}

TREE rb_insert(TREE r, NODE *z)
{
TREE x, y;
x = r;
y = NULL;
while(x != NULL)
{
y = x;
if(z->key < x->key)
x = x->left;
else
x = x->right;
}

z->p = y;
if(y == NULL)
{
r = z;
}
else if(z->key < y->key)
{
y->left = z;
}
else
{
y->right = z;
}
z->left = NULL;
z->right = NULL;
z->color = 0;

r = rb_insert_fixup(r, z);
return r;
}

TREE rb_create(int arr[], int n)
{
TREE r, tmp;
int i;
r = NULL;
for(i=0; i<n; i++)
{
tmp = (TREE)malloc(sizeof(NODE));
tmp->key = arr[i];
r = rb_insert(r, tmp);
}
return r;
}
void print_pre(TREE r)
{
if(r == NULL)
return ;
if(r->color == 0)
printf("%d:red  ", r->key);
else
printf("%d:black  ", r->key);
print_pre(r->left);
print_pre(r->right);
}
void print_mid(TREE r)
{
if(r == NULL)
return ;
print_mid(r->left);
if(r->color == 0)
printf("%d:red  ", r->key);
else
printf("%d:black  ", r->key);
print_mid(r->right);
}
int main()
{
TREE r;
int n = 6, arr[6]={41, 38, 31, 12, 19, 8};
//create a rb tree
r = rb_create(arr, n);

printf("----------先序遍历----------:\n");
print_pre(r);

printf("\n\n----------中序遍历----------:\n");
print_mid(r);
printf("\n\n----------End line----------\n");

return 0;
}


2、旋转

现在我们介绍一下刚才用到的旋转,旋转有两种:左旋和右旋。其左旋和右旋过程分别图下图所示。



图2.1 右旋

右旋的C代码如下:

TREE right_rotate(TREE r, NODE *x)
{
TREE y = x->left;
x->left = y->right;
if(y->right != NULL)
y->right->p = x;
y->p = x->p;
if(x->p == NULL)
r = y;
else if(x->p->left = x)
x->p->left = y;
else
x->p->right = y;
y->right = x;
x->p = y;

return r;
}




图2.2 左旋

左旋的C代码如下:

TREE left_rotate(TREE r, NODE *x)
{
TREE y = x->right;
x->right = y->left;
if(y->left != NULL)
y->left->p = x;
y->p = x->p;
if(x->p == NULL)
r = y;
else if(x == x->p->left)
x->p->left = y;
else
x->p->right = y;
y->left = x;
x->p = y;

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