参考博文:http://www.cnblogs.com/skywang12345/p/3245399.html
红黑树的5个特性:
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
此处性质(1)、(2)都比较好理解,(3)所说的叶子节点实际上是空节点,将空接待你看成黑色的。
性质(4)则表示如果一个节点只有一个孩子节点且此节点是红色的,那么他的孩子节点必然只有一个红色节点,
否则会不满足性质(5),性质5是红黑树最重要的一点,也是保证复杂度平衡的关键,所以每次旋转时都要
保证性质(5)满足。
参考博文中插入和删除都只考虑一边子树的情况,在这里将需要旋转的情况列出:
关于旋转操作请参考:http://blog.csdn.net/wsnbb123456789/article/details/53192406
这里只是列出一部分情况,并不是全部,详细可以看参考博wen。
notice:再次提醒在进行旋转时一定要保证性质(5)是满足的,在删除调整时,删除的是黑节点,
所以要记得所在的子树少了一个黑节点了,已经违反了性质(5),所以调整考虑这点就可以了。
插入调整(父节点为红色,祖父节点为黑色):
1、叔叔节点为红色
将父节点和叔叔节点变为黑色,祖父节点变成红色,更改调整节点为祖父节点
2、叔叔节点为黑色
将父节点变成黑色,祖父节点变成红色
(1)、当前节点为父节点的右节点,当前节点变为父节点,对父节点进行左旋
(2)、将父节点变为黑色,祖父节点变为红色,当前节点不变
当前节点为父节点的左节点,根据父节点是祖父节点左孩子还是右孩子
进行旋转,如果左孩子向右,右孩子就向左旋转
notice:实际上第2种情况最终会将当前节点和父节点都变成各自父亲的左孩子,
在进行一次右旋就调整成符合红黑树的结构了
删除调整:
将当前节点看成两种颜色"黑+黑",删除的节点颜色是红色就不需要调整
如果是黑色,则此子树的黑节点少1,所以加到当前节点然后再做相应的调整
设当前节点的兄弟节点为B,父节点为F,则分成以下几种情况:
1、B为红色,F肯定为黑色,将B变为黑色,F变为红色,将B旋转为子树的根节点
(下面所有情况没有特意讲当前节点变化就是不变的),变成情况2 3
2、B为黑色,如果B的两个儿子节点都为黑色,则直接将B变成红色,当前节点变成F
3、B为黑色,如果B的两个儿子节点不都为黑色,分成两种情况:
(1)当前节点为F的左节点,在分成以下两种情况:
1.1 B的左节点为红色,右节点为黑色,则将左节点变成黑色
B变成红色,然后将左节点旋转为根(即右旋),变成1.2的情况
1.2 B的右节点为红色,则将右节点变成黑色,B和F的颜色交换,对F进行左旋
调整结束
(2)当前节点为F的右节点,在分成以下两种情况:
2.1 B的左节点为黑色,右节点为红色,将右节点变成黑色
B变成红色,然后对B进行左旋,变成2.2情况
2.2 B的左节点为红色,则将左节点变成黑色,B和F颜色交换,对F进行右旋
调整结束
由于红黑树相对来说代码比较长,容易出错,所以写完最好自己产生随机数据,用set验证正确性,
也可以用http://poj.org/problem?id=3481验证
#include <cstdio>
#include <queue>
using namespace std;
#define RED 0
#define BLACK 1
#define DBUG printf ( "###\n" )
struct RB
{
int x, color, id;
struct RB * ch[2], * parent;
};
struct RB * null, * rt;
/*
插入调整(父节点为红色,祖父节点为黑色):
1、叔叔节点为红色
将父节点和叔叔节点变为黑色,祖父节点变成红色,更改调整节点为祖父节点
2、叔叔节点为黑色
将父节点变成黑色,祖父节点变成红色
(1)、当前节点为父节点的右节点,当前节点变为父节点,对父节点进行左旋
(2)、将父节点变为黑色,祖父节点变为红色,当前节点不变
当前节点为父节点的左节点,根据父节点是祖父节点左孩子还是右孩子
进行旋转,如果左孩子向右,右孩子就向左旋转
notice:实际上第2种情况最终会将当前节点和父节点都变成各自父亲的左孩子,
在进行一次右旋就调整成符合红黑树的结构了
*/
RB * sibling ( RB * T, RB * s )
{
return T->ch[0] == s ? T->ch[1] : T->ch[0];
}
//0:左旋 1:右旋
void Rotate ( RB * &T, int k )
{
RB * o = T->ch[k^1];
o->parent = T->parent;
if ( T->parent != NULL )
T->parent->ch[ T->parent->ch[1] == T ] = o;
T->parent = o;
if ( o->ch[k] != null )
o->ch[k]->parent = T;
T->ch[k^1] = o->ch[k];
o->ch[k] = T;
T = o;
if ( T->parent == NULL )
rt = T;
}
void insertAdjust ( RB * &tmp )
{
RB * T = tmp;
while ( T->parent != NULL && T->parent->color == RED )
{
RB * p = sibling ( T->parent->parent, T->parent );
if ( p->color == RED )
{
T->parent->color = p->color = BLACK;
T->parent->parent->color = RED;
T = T->parent->parent;
}
else
{
if ( T == T->parent->ch[1] )
{
T = T->parent;
Rotate ( T, 0 );
T = T->ch[0];
}
else
{
T->parent->color = BLACK;
T->parent->parent->color = RED;
RB * save = T, * s = T->parent;
T = T->parent->parent;
if ( T->ch[0] == s )
{
Rotate ( T, 1 ); //end
break ;
}
else
{
Rotate ( T, 0 );
T = save; //continue
}
}
}
}
}
void insert ( RB * &T, int x, int id )
{
RB * p = T, * q = NULL;
while ( p != null )
{
q = p;
if ( p->x == x )
return ;
if ( p->x > x )
p = p->ch[0];
else
p = p->ch[1];
}
RB * o = new RB ( );
o->x = x;
o->id = id;
o->color = RED;
o->parent = q;
o->ch[0] = o->ch[1] = null;
if ( q != NULL )
q->ch[ q->x <= x ] = o;
if ( q == NULL )
{
T = o;
return ;
}
if ( q->color == BLACK )
return ;
insertAdjust ( o );
}
RB * find_del ( RB * &T )
{
RB * p = T->ch[0];
while ( p->ch[1] != null )
{
p = p->ch[1];
}
T->x = p->x;
return p;
}
/*
删除调整:
将当前节点看成两种颜色"黑+黑",删除的节点颜色是红色就不需要调整
如果是黑色,则此子树的黑节点少1,所以加到当前节点然后再做相应的调整
设当前节点的兄弟节点为B,父节点为F,则分成以下几种情况:
1、B为红色,F肯定为黑色,将B变为黑色,F变为红色,将B旋转为子树的根节点
(下面所有情况没有特意讲当前节点变化就是不变的),变成情况2 3
2、B为黑色,如果B的两个儿子节点都为黑色,则直接将B变成红色,当前节点变成F
3、B为黑色,如果B的两个儿子节点不都为黑色,分成两种情况:
(1)当前节点为F的左节点,在分成以下两种情况:
1.1 B的左节点为红色,右节点为黑色,则将左节点变成黑色
B变成红色,然后将左节点旋转为根(即右旋),变成1.2的情况
1.2 B的右节点为红色,则将右节点变成黑色,B和F的颜色交换,对F进行左旋
调整结束
(2)当前节点为F的右节点,在分成以下两种情况:
2.1 B的左节点为黑色,右节点为红色,将右节点变成黑色
B变成红色,然后对B进行左旋,变成2.2情况
2.2 B的左节点为红色,则将左节点变成黑色,B和F颜色交换,对F进行右旋
调整结束
*/
void removeAdjust ( RB * &tmp )
{
RB * T = tmp;
while ( T->parent != NULL && T->color != RED )
{
RB * B = sibling ( T->parent, T );
RB * F = T->parent;
if ( B->color == RED )
{
B->color = BLACK;
F->color = RED;
RB * save = T;
T = T->parent;
Rotate ( T, T->ch[0] == B );
T = save;
}
else
{
if ( B->ch[0]->color == BLACK && B->ch[1]->color == BLACK )
{
B->color = RED;
T = T->parent;
}
else if ( F->ch[0] == T )
{
if ( B->ch[0]->color == RED && B->ch[1]->color == BLACK )
{
B->ch[0]->color = BLACK;
B->color = RED;
Rotate ( B, 1 );
}
else if ( B->ch[1]->color == RED )
{
B->color = F->color;
F->color = BLACK;
B->ch[1]->color = BLACK;
Rotate ( F, 0 );
return ;
}
}
else if ( F->ch[1] == T )
{
if ( B->ch[0]->color == BLACK && B->ch[1]->color == RED )
{
B->ch[1]->color = BLACK;
B->color = RED;
Rotate ( B, 0 );
}
else if ( B->ch[0]->color == RED )
{
B->color = F->color;
F->color = BLACK;
B->ch[0]->color = BLACK;
Rotate ( F, 1 );
return ;
}
}
}
}
T->color = BLACK;
}
void remove ( RB * &tmp, int x )
{
RB * T = tmp;
while ( T != null )
{
if ( T->x == x )
break ;
if ( T->x > x )
T = T->ch[0];
else
T = T->ch[1];
}
if ( T == null )
return ;
RB * p, * q = T->parent;
if ( T->ch[0] == null && T->ch[1] == null )
{
if ( q == NULL )
{
rt = null;
delete T;
return ;
}
else
{
p = T;
}
}
else if ( T->ch[0] == null )
{
if ( q == NULL )
{
rt = T->ch[1];
T->ch[1]->parent = NULL;
delete T;
return ;
}
else
{
q->ch[ q->ch[1] == T ] = T->ch[1];
T->ch[1]->parent = q;
T->ch[1]->color = BLACK;
delete T;
return ;
}
}
else if ( T->ch[1] == null )
{
if ( q == NULL )
{
rt = T->ch[0];
T->ch[0]->parent = NULL;
delete T;
return ;
}
else
{
q->ch[ q->ch[1] == T ] = T->ch[0];
T->ch[0]->parent = q;
T->ch[0]->color = BLACK;
delete T;
return ;
}
}
else
{
p = find_del ( T );
}
RB * F = p->parent;
if ( p->ch[0] != null && p->ch[0]->color == RED ||
p->ch[1] != null && p->ch[1]->color == RED )
{
if ( p->ch[0] != null )
{
p->ch[0]->color = BLACK;
F->ch[ F->ch[1] == p ] = p->ch[0];
p->ch[0]->parent = F;
}
else if ( p->ch[1] != null )
{
p->ch[1]->color = BLACK;
F->ch[ F->ch[1] == p ] = p->ch[1];
p->ch[1]->parent = F;
}
else
{
F->ch[ F->ch[1] == p ] = null;
}
delete p;
return ;
}
removeAdjust ( p );
if ( p->ch[0] != null )
{
F->ch[ F->ch[1] == p ] = p->ch[0];
p->ch[0]->parent = F;
}
else if ( p->ch[1] != null )
{
F->ch[ F->ch[1] == p ] = p->ch[1];
p->ch[1]->parent = F;
}
else
{
F->ch[ F->ch[1] == p ] = null;
}
delete p;
}
void print ( RB * T )
{
queue < RB * > q;
q.push ( T );
while ( ! q.empty ( ) )
{
RB * p = q.front ( );
q.pop ( );
printf ( "%d %s\n", p->x, p->color ? "BLACK" : "RED" );
if ( p != null )
{
q.push ( p->ch[0] );
q.push ( p->ch[1] );
}
}
}
void dfs ( RB * T )
{
if ( T != null )
{
dfs ( T->ch[0] );
printf ( "%d\n", T->x );
dfs ( T->ch[1] );
}
}
void solve ( )
{
//freopen ( "1.txt", "r", stdin );
//freopen ( "3.txt", "w", stdout );
null = new RB ( );
null->color = BLACK;
null->x = -1;
null->parent = NULL;
null->ch[0] = null->ch[1] = null;
rt = null;
int x, id, op, n;
while ( ~ scanf ( "%d", &op ), op )
{
if ( op == 1 )
{
scanf ( "%d%d", &id, &x );
insert ( rt, x, id );
}
else if ( op == 2 )
{
if ( rt == null )
printf ( "0\n" );
else
{
RB * p = rt;
while ( p->ch[1] != null )
p = p->ch[1];
printf ( "%d\n", p->id );
remove ( rt, p->x );
}
}
else
{
if ( rt == null )
printf ( "0\n" );
else
{
RB * p = rt;
while ( p->ch[0] != null )
p = p->ch[0];
printf ( "%d\n", p->id );
remove ( rt, p->x );
}
}
rt->color = BLACK;
//print ( rt );
}
}
int main ( )
{
solve ( );
return 0;
}
此处实现并不一定正确,可能有些步骤没验证到,请谨慎使用。
随机数据代码(只测试最后结果是否正确):
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int a[maxn];
set < int > v;
void solve ( )
{
//freopen ( "1.txt", "w", stdout );
srand ( time ( NULL ) );
int n, pos = 0;
n = 100005;
printf ( "%d\n", n );
while ( n -- )
{
//1:表示插入 2:表示删除
printf ( "%d %d\n", rand ( )%2+1, rand ( )%10005 );
}
}
int main ( )
{
solve ( );
return 0;
}
使用set验证是否正确
#include <bits/stdc++.h>
using namespace std;
set < int > vis;
int main ( )
{
int n, op, x;
//freopen ( "1.txt", "r", stdin );
//freopen ( "2.txt", "w", stdout );
scanf ( "%d", &n );
for ( int i = 0; i < n; i ++ )
{
scanf ( "%d%d", &op, &x );
if ( op == 1 )
{
if ( vis.count ( x ) == 0 )
vis.insert ( x );
}
else
{
if ( vis.count ( x ) )
vis.erase ( x );
}
}
for ( set < int > :: iterator it = vis.begin ( ); it != vis.end ( ); it ++ )
printf ( "%d\n", *it );
return 0;
}
在此推荐使用WinMerge软件,判断两个输出文件的结果是否相等,那样比较方便的验证自己代码的正确性了。