紅黑樹(沒有一條路徑會比其他路徑長2倍)是一種平衡二叉樹,它在計算機中被廣泛的應用!
結點採用的結構:
struct RB_node{
int key;
int color;
struct RB_node *p,*left,*right;//父,左,右
}
一、牢記紅黑樹的五大性質。
紅黑樹的五大性質是學習紅黑樹最重要最基礎的,一定要背下來,方便在插入刪除的時候保持這五大性質。
1、每個結點的顏色是紅色或者黑色。
2、根結點是黑色。
3、每個葉結點都是黑色
4、紅色結點的子結點都是黑色。
5、對每一個結點,從該結點到其後代所有的葉子結點的簡單路徑上,都包含相同數目的黑色結點。
說明:根結點的父結點和所有葉子結點的子結點都指向同一個哨兵結點T.nil,當然這個哨兵結點的顏色爲黑色。
二、旋轉操作
left-rotate(T,x)
{
y=x.right;
x.right=y.left
y.p=x.p //孩子找父親是唯一的
if(x.p==T.nil) //父親找孩子就會有多種情況
T.root=y
else if(x==x.p.left)
x.p.left=y
else x.p.right=y
y.left=x
x.p=y
}
右旋只需要對換right<->left三、插入操作
插入操作與一般二叉樹的操作類似RB_insert(T,z)
{
y=T.nil
x=T.root
while(x!=T.nil)//找z的父結點
y=x
if(z.key>x.key)
x=x.right
else
x=x.left
z.p=y
if(y==T.nil)//z爲父結點的左還是右子結點
T.root=z
else if(y.key>z.key)
y.left=z
else
y.right=z
z.color="red"//初始爲red
z.left=nil
z.left=nil
RB_insert_fixup(T,z)//調用調整程序以保持樹的性質
}
四、調整
需要調整的情況是:當它的父結點是red時由此可以分爲8種情況:前4種與後4種左右對稱
前4種:
後4種:
與前4種完全對稱
算法:
RB_insert_fixup(T,z)//見算法導論179頁
while(z.p.color=="red")
if(z.p.p.left=z.p)//z.p.p.left=z.p前4種
y=z.p.p.right;
if(y.color=="red") //case1
y.color="black";
z.p.color="black";
z.p.p.color="red";
z=z.p.p;
else
if(z.p.right==z)//case2
z=z.p;
LEFT_rotate(T,z);
z.p.color="black";//case3
z.p.p.color="red";
RIGHT_rotate(T,z.p.p);
else //z.p.p.right=z.p後4種
y=z.p.p.left;
if(y.color=="red") //case1
y.color="black";
z.p.color="black";
z.p.p.color="red";
z=z.p.p;
else
if(z=z.p.left)//case2
z=z.p;//!!!
RIGHT_rotate(T,z);
z.p.color="black";//case3
z.p.p.color="red";
LEFT_rotate(T,z.p.p);
T.root.color="black";
五、代碼實現
//如果要多值進行修改傳遞指針即可,如果要對不是全局變量指針進行修改要加&
#include <iostream>
using namespace std;
struct RB_node{//結點
string color;
int key;
RB_node *p;
RB_node *left;
RB_node *right;
RB_node(int k):key(k){}
RB_node():left(NULL),right(NULL),p(NULL),color("black"){};//nil專用初始化函數
};
struct RB_tree{
RB_tree()//初始化
{
nil=new RB_node();
root=nil;
}
void Inorder(RB_node * k)
{
printf("%2d %6s ",k->key,(k->color).c_str() );
if(k->left!=nil)printf("L:%2d",(k->left)->key);
else printf("L:-1");
if(k->right!=nil)printf(" R:%2d",(k->right)->key);
else printf(" R:-1");
cout<<endl;
if(k->left!=nil)Inorder(k->left);
if(k->right!=nil)Inorder(k->right);
}
RB_node *root;//根結點
RB_node *nil;//集中所有頭的結點
};
void RB_insert_fixup(RB_tree &T,RB_node *z);//插入的調整 見算法導論178頁
void LEFT_rotate(RB_tree &T,RB_node *z);//左旋
void RIGHT_rotate(RB_tree &T,RB_node *z);//右旋
void RB_insert(RB_tree &T,RB_node *z)//不加&
{
RB_node *y,*x;
y=T.nil; //最終是要記錄z的父結點
x=T.root;
while(x!=T.nil)
{
y=x; //將x賦給y,x繼續下移,直到爲nil
if(z->key>x->key)x=x->right;
else x=x->left;
}
z->p=y; //設置z的父結點
//判斷z是父結點的左還有右子結點
if(y==T.nil)
T.root=z;
else if(y->key>z->key)
y->left=z;
else
y->right=z;
z->color="red";//開始爲red
z->left=T.nil;
z->right=T.nil;
RB_insert_fixup(T,z);//如果y紅色,破壞性質(因爲z也是red),調整
}
void RB_insert_fixup(RB_tree &T,RB_node *z)//見算法導論179頁
{
while(z->p->color=="red")
{
RB_node *y;
if(z->p->p->left==z->p)//z.p.p.left=z.p
{
y=z->p->p->right;
if(y->color=="red") //case1
{
y->color="black";
z->p->color="black";
z->p->p->color="red";
z=z->p->p;
}
else
{
if(z->p->right==z)//case2
{
z=z->p;//!!!
LEFT_rotate(T,z);
}
z->p->color="black";//case3
z->p->p->color="red";
RIGHT_rotate(T,z->p->p);
}
}
else //z.p.p.right=z.p
{
y=z->p->p->left;
if(y->color=="red") //case1
{
y->color="black";
z->p->color="black";
z->p->p->color="red";
z=z->p->p;
}
else
{
if(z=z->p->left)//case2
{
z=z->p;//!!!
RIGHT_rotate(T,z);
}
z->p->color="black";//case3
z->p->p->color="red";
LEFT_rotate(T,z->p->p);
}
}
}
T.root->color="black";
}
void LEFT_rotate(RB_tree &T,RB_node *x)//左旋 見算法導論177頁
{
RB_node *y;
y=x->right;//set y
x->right=y->left;
if(y->left!=T.nil)//y->left==T.nil時不用設置T.nil的父結點
y->left->p=x;
y->p=x->p;
if(x->p==T.nil)//如果x是root結點,旋轉後y爲root結點
T.root=y;
else if(x->p->left==x)//y 是父結點的左還是右
x->p->left=y;
else
x->p->right=y;
y->left=x; //put x on y's left
x->p=y;
}
//將LEFT_rotate中right<---->eft ,就可以得到右旋
void RIGHT_rotate(RB_tree &T,RB_node *x)//右旋
{
RB_node *y;
y=x->left;//set y
x->left=y->right;
if(y->right!=T.nil)//y->right==T.nil時不用設置T.nil的父結點
y->right->p=x;
y->p=x->p;
if(x->p==T.nil)//如果x是root結點,旋轉後y爲root結點
T.root=y;
else if(x->p->right==x)//y 是父結點的左還是右
x->p->right=y;
else
x->p->left=y;
y->right=x; //put x on y's right
x->p=y;
}
主函數:int main()
{
RB_tree T;
cout <<"T.nil = "<<T.nil<<endl;
int list[]={26,17,41,14,21,30,47,10,16,19,23,28,38,7,12,15,20,35,39,3};
//int list[]={10,9,15,14};
for(auto ia:list)
{
RB_node *p=new RB_node(ia);
RB_insert(T,p);
}
T.Inorder(T.root);
return 0;
}
運行結果:
26 black L: 17 R: 41
17 red L: 14 R: 21
14 black L: 10 R: 16
10 red L: 7 R: 12
7 black L: 3 R:nil
3 red L:nil R:nil
12 black L:nil R:nil
16 black L: 15 R:nil
15 red L:nil R:nil
21 black L: 19 R: 23
19 black L:nil R: 20
20 red L:nil R:nil
23 black L:nil R:nil
41 black L: 30 R: 47
30 red L: 28 R: 38
28 black L:nil R:nil
38 black L: 35 R: 39
35 red L:nil R:nil
39 red L:nil R:nil
47 black L:nil R:nil
由此可以畫出這顆紅黑樹:刪除 未完待續......