紅黑樹是一種二叉查找樹。
所謂二叉查找樹:首先是一個二叉樹;其次每一個節點的左節點值是小於此節點的值,每一個右節點的值大於此節點的值。
紅黑樹也是一種平衡二叉查招樹,平衡性是指左右子樹的節點個數相差不大,且樹的深度一般保持在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*;
這裏來開始找插入位置,紅黑樹是一種查找樹,那麼在節點的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 ;
}