红黑树是一种二叉查找树。
所谓二叉查找树:首先是一个二叉树;其次每一个节点的左节点值是小于此节点的值,每一个右节点的值大于此节点的值。
红黑树也是一种平衡二叉查招树,平衡性是指左右子树的节点个数相差不大,且树的深度一般保持在log(n)。n为树中节点数。
红黑树的定义:
1.节点只有红黑两种节点
2.根节点必须为黑节点
3.红节点的左右子节点都为黑节点
4.每个节点到其子树叶子节点的每一条路径中的黑节点数相同
这里可以多加一条,如果我们认为NULL为叶子节点的话:5.每一个叶子节点的颜色都是黑色的。
下图就是一棵红黑树:
知道了这些性质后,我们需要的就是去怎么构造这样一颗树来。
在此之前,先证明一下这个中红黑树的平衡性怎么样?即要搞清楚这个棵树在节点n时,树的深度为多少。否则,造出来的树性能不好,那也是白搭。
证明:对于一颗为n个节点的红黑树,树的深度最多为2*log(n+1).
首先,该怎么下手去证明呢?
我们想到红黑树的性质,这也是证明的一般形式了。红黑树中,从根节点到叶子节点的每一条路径的黑节点相同,我们可以记每一条路径上的黑节点数为bh(t)。其实bh(t)我们可以认为树的黑高度。
有了这个事实,我们就可以推算红黑树中至少有多少个节点。对于全是黑节点,我们有二叉树的性质可得树的节点数至少为:n>= 2^bh(t) - 1 。我们可以用归纳假法来验证:首先要验证,当bh(t) = 1时,此时我们得到的只有跟节点为黑,那么这个数至少有1个节点,代入公式得到:2^1-1=1。初始条件满足后,我们假设在bh(t)=k是满足这个不等式,那么退到k+1时,有:对于一个节点x,其黑高度为bh(t) = k + 1,那么它到以x为根的子树的黑高对为k+1或者k,我们选择一个小的高度这样数就会是最小的,那么对于左右子树(到个叶子节点的黑节点相同,所以必有左右子树)的最少节点数为:2^k-1;两边相加得到这个n>= 2^k-1+2^k-1+1(x本身);即得到n>=2^(k+1)-1.
我们再有,每一个红节点的子节点都为黑,那么在一条路径上其黑节点数至少为一半,那么对于一颗高度为h的红黑树来说,其黑高度至少为h/2,那么
节点数至少为:n>=2^h/2-1;所以对于一个节点为n的红黑树其高度最多为:h<=2*log(n+1)。
其中一条真理就是:每条路径的黑节点数必须相等。这个性质限制了其树的高度。
到此,也算是完成了记忆红黑树的性质了。那么接下来就是要怎么样去造一颗红黑树了。
其实树的构造都是由插入来构造的,我们能够进行树的插入操作,我们就能够构造一棵树。那么插入操作会是什么样的呢?
第一,我们需要定位插入操作的地方。
先设计一下红黑树的节点:
typedef struct Node{
int key ;//这里只先关注int类型
struct Node * parent ;
struct Node * left ;
struct Node * rigth ;
int color ;
}RBT*;
}RBT*;
这里来开始找插入位置,红黑树是一种查找树,那么在节点的left,都是小于节点的;在节点的right,都是大于节点的。根据这个规律可以再log(n)的时间找到这个该插入的节点。代码如下:
RBT cur = root ;//首先,找到树的根节点,然后按照上述规则找就是了。
while(cur != NULL )
{
if(key < cur->key)
{
root = cur ;
cur = cur ->left ;
}else
{
root = cur ;
cur = cur->right ;
}
}
位置找到了,就可以开始插入了,下面是插入的代码:
if(key < root->key)
{//left
Node*tmp = new Node;
tmp->key = key ;
tmp->parent = root ;
tmp->left = NULL ;
tmp->right= NULL ;
tmp->color = 1 ;
root->left = tmp ;
}else
{
Node*tmp = new Node;
tmp->key = key ;
tmp->parent = root ;
tmp->left = NULL ;
tmp->right= NULL ;
tmp->color = 1 ;
root->right = tmp ;
}
所有的插入节点,都开始调整为红节点,然后根据树的情况来调整。下面来看在插入节点后,会造成红黑树那些性质被打破。
1,插入节点为根节点时,那么这个节点应该要调整为黑。这个其实一下就调整了,是不用管的。
2,插入节点不为根,但是其父节点颜色为红,那么就违背了第3条规则:所有红节点的子节点都为黑。重点是这个违反条件。下面看一个列子:
我们这里插入了4,如果key处所标注的。
那么还有哪些情况呢,当然一定要是父节点为红才行,如果父节点不为红而为黑,此时我们是不用调整的。那么此时我们该怎么调整让它达到平衡呢?因为,5节点为红,且8节点也为红,我们同时调整这两个点为黑,那么在7这个节点的两边(左子树和右子树)的黑节点都会加1,但是其它条件都会满足,所以为了防止第4条规定被违反,我们直接调整7节点变为红就行了。调整后的图如下:
那么此时我们的key(违反规则的点)变为7这个节点,此时情况不是上述的情况了。这种归为情况2.那么该怎么调整情况2呢?那就一步遍遍的试了。调整2为黑?那可不行啊,这样直接的加重了这边黑节点数了。但这了有个出发点,就是2如果是整颗树的根节点呢?那么调整2节点为黑是不是所有情况就都满足了?为了保持第4条约束,此时可以去调整14为红,然后在每条边上都减少一个黑节点数。但是这种方法太复杂,要去遍历一遍所有的树路径,然后还要选取哪个点来变颜色。算法应该就在此节点想了。如果可以转动树就好了。我们把2转动一下。
如果我们转动2,而不改变任何颜色,我们来看看是什么效果:
此时将会发生2这个节点是违反了规定,由于旋转的是两个红节点,那么对于每条路径的黑节点数是不会违反的。如果此时我们将7节点变为黑,然后8节点变为红,那么在7以下的节点都不会违反规定了。但是,7变黑增加了7->2->...后面路径的黑点数了,因此不可取。那么,我们如果只是将7节点变黑呢?那么8节点此时7这个节点下的子树都不会违反规定了,但是想想,7上面的会违法规定了,此时的补救措施就是将7的父节点11变为红,但是,还是没能够平衡节点11右边子树的黑节点数,此时,我们可以旋转,即对节点11进行旋转,把7转上去,11转下去,这样就平衡了:
由此,我们可以总结:
对于插入,可能违反的条件就只有其父节点也为红色节点,否则节点是不会违反任何限制条件的。那么对于违反了条件3,我们应该怎么样调整呢?
首先,要知道插入节点上面是红色父节点后其父节点的两个孩子都是空的。
其次,父节点为红,如果父节点的兄弟节点为红我们可以将父节点和其兄弟节点变黑,然后父节点的父节点变红,这样在父节点的父节点下面的子树的红黑树性质都不会改变。但是,这中情况会在父节点的父节点处违反红黑树性质。因此,我们需要再往上检查。
第三,父节点为红,而其兄弟节点为黑,此时情况会怎么样呢?我们得明确一点,对于插入前红黑树是平衡的,所以,插入节点的父节点是红的,那么其父节点的父节点就一定会是黑的。如果此时只是将父节点变黑会导致父节点这边的黑节点数比其兄弟节点的黑节点数多,所以不能将父节点变黑,那么怎么样呢?一个思想:将红节点(本身违反规定节点和违反规定节点的父节点)变黑,然后某个黑节点变红,这样可以研究一下违反规定节点和其父节点及父节点的父节点就能知道该怎么调整了。为了调整这个平衡我们需要做旋转。其真正的目的是:将此时违反规定的节点变黑,此时的父节点的父节点变红,然后将此时违反规定的节点旋转到父节点的父节点处,我们就将红黑树调整平衡了。
今天想了想:其实调整只要找一个方向就好了,就是将违反规定的节点的父节点给弄黑就行了。然后,将父节点弄黑后怎么样调整到一个平衡。
下面是代码:
//red-black-tree
#include <iostream>
using namespace std ;
enum COLOR{BLACK,RED};
typedef struct Node{
int key ;
struct Node * parent ;
struct Node * left ;
struct Node * right ;
COLOR color ;//0 stand black , 1 stand red .
}*RBT;
void LEFT_ROATE(RBT *T,Node *x);
void RIGHT_ROATE(RBT *T,Node *x);
void RBT_insert_fix(RBT *T,Node *x)
{
if(T == NULL || (*T) == NULL ||x == NULL )
{
return ;// if null, is error.
}
while (x->parent != NULL && x->parent->color == RED)
{
Node *xp = x->parent ;
Node *xpp = xp->parent;
if(xp == xpp->left)// xp is xpp left child .
{
COLOR bcolor = BLACK ;
Node *brother = NULL ;
if(xp == xpp->left)
{
if(xpp->right == NULL)
{
bcolor = BLACK ;
}else
{
bcolor = xpp->right->color ;
}
brother = xpp->right;
}else
{
if (xpp->left == NULL )
{
bcolor = BLACK ;
}else
{
bcolor = xpp->left->color ;
}
brother = xpp->left;
}
if(bcolor == RED)
{//case 1
brother->color = BLACK;
xp->color = BLACK ;
xpp->color = RED ;
x = xpp ;
}else
{//case 2 3
if(x == xp->right)
{
x = xp ;
LEFT_ROATE(T,x);
xp = x->parent;
}
xp->color = BLACK ;
xpp->color = RED ;
RIGHT_ROATE((T),xpp);
}
}// if xp == xpp->left
else // if xp is xpp right child .
{
COLOR bcolor = BLACK ;
Node *brother = NULL ;
if(xp == xpp->left)//case 1.
{
if(xpp->right == NULL)
{
bcolor = BLACK ;
}else
{
bcolor = xpp->right->color ;
}
brother = xpp->right;
}else
{
if (xpp->left == NULL )
{
bcolor = BLACK ;
}else
{
bcolor = xpp->left->color ;
}
brother = xpp->left;
}
if(bcolor == RED)
{//case 1
brother->color = BLACK;
xp->color = BLACK ;
xpp->color = RED ;
x = xpp ;
}else
{//case 2 3
if(x == xp->left)
{
x = xp ;
RIGHT_ROATE(T,x);
xp = x->parent;
}
xp->color = BLACK ;
xpp->color = RED ;
LEFT_ROATE((T),xpp);
}
}
}
(*T)->color = BLACK ;
}
void LEFT_ROATE(RBT *T,Node *x)
{
if(T == NULL || x == NULL || (*T) == NULL )
{
return ;
}
if(x->right == NULL)
{
return ;
}
Node *xright = x->right ;
Node *xparent= x->parent;
xright->parent = xparent;
x->right = xright -> left ;
if(xright->left != NULL )
{
xright->left->parent = x ;
}
x->parent = xright ;
xright->left = x ;
if( xparent != NULL && xparent->left == x)
{
xparent->left = xright ;
}else if(xparent != NULL)
{
xparent->right = xright ;
}else // xparent is null , so x is root .
{
(*T) = xright ;
}
}
void RIGHT_ROATE(RBT *T,Node *x)
{
if(T == NULL || x == NULL || (*T) == NULL )
{
return ;
}
if(x->left == NULL )
{
return ;
}
Node * xleft = x->left ;
Node * xparent = x->parent ;
xleft ->parent = xparent ;
x->left = xleft->right ;
if(xleft->right != NULL )
{
xleft->right->parent = x ;
}
if(xparent != NULL && x == xparent->left)
{
xparent->left = xleft ;
}else if(xparent != NULL)
{
xparent ->right = xleft ;
}else // xparent == NULL ,so x is root .
{
(*T) = xleft ;
}
xleft ->right = x ;
x->parent = xleft ;
}
int RBT_insert(RBT *T, int key)
{
if(T == NULL )
{
return 0;//插入不成功
}
if((*T) == NULL )
{
(*T) = new Node;
(*T)->key = key ;
(*T)->parent = NULL ;
(*T)->left = NULL ;
(*T)->right = NULL ;
(*T)->color = BLACK ;// black
return 1 ;
}
RBT root = *T ;// root node.
// find the right position.
RBT cur = root ;
while(cur != NULL )
{
if(key < cur->key)
{
root = cur ;
cur = cur ->left ;
}else
{
root = cur ;
cur = cur->right ;
}
}
//insert
Node *tmp = new Node;
if(key < root->key)
{//left
tmp->key = key ;
tmp->parent = root ;
tmp->left = NULL ;
tmp->right= NULL ;
tmp->color = RED ;
root->left = tmp ;
}else
{
tmp->key = key ;
tmp->parent = root ;
tmp->left = NULL ;
tmp->right= NULL ;
tmp->color = RED ;
root->right = tmp ;
}
RBT_insert_fix(T,tmp);
return 1 ;
}
void printRBT (RBT T)
{
if(T == NULL )
{
return ;
}
// mid print
printRBT(T->left);
cout<<T->key<<":";
if(T->color == BLACK)
cout<<"black"<<endl;
else
cout<<"red"<<endl ;
printRBT(T->right);
}
void printPreRBT (RBT T)
{
if(T == NULL )
{
return ;
}
// pre print
cout<<T->key<<":";
if(T->color == BLACK)
cout<<"black"<<endl;
else
cout<<"red"<<endl ;
printPreRBT(T->left);
printPreRBT(T->right);
}
void create_RBT(RBT *T , int *key, int start ,int end )
{
if(T== NULL || key == NULL || start < 0 || end < start)
{
return ;
}
for(int i = start ; i <= end ; i ++ )
{
RBT_insert(T,key[i]);
/* cout<<i<<"mid"<<endl;
printRBT(*T);
cout<<i<<"pre"<<endl;
printPreRBT(*T);
*/
}
}
int main()
{
Node *t = NULL ;
RBT *T = &t ;
int array[10] = {10,8,5,6,7,4,3,1,2,9};
create_RBT(T,array,0,9);
cout<<"mid print"<<endl;
printRBT(*T);
cout<<"pre print"<<endl;
printPreRBT(*T);
system("pause");
return 0 ;
}