第十三章 紅黑樹

紅黑樹(沒有一條路徑會比其他路徑長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
由此可以畫出這顆紅黑樹:


刪除 未完待續......

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章