紅黑樹

下面網址中是紅黑樹很好的教材,很詳細。node

http://blog.csdn.net/eric491179912/article/details/6179908算法

另外經典教材就是算法導論中關於紅黑樹的章節了,以算法導論爲主,實在不明白的地方再去網上查找資料。測試

紅黑樹是一種特殊的二叉查找樹,你們都知道二叉查找樹的複雜度最壞狀況爲O(h),爲了減低最壞狀況的複雜度,大神們設計了許多種二叉查找樹的改進版本,紅黑樹即是其中之一,它能夠保證在最壞狀況下複雜度爲O(lgn),也就是說h < lgn。spa

紅黑樹的五個性質.net

1)每一個節點或者是紅色,或者是黑色設計

2)根節點是黑色3d

3)每一個葉子節點是黑色的(此處的葉子節點指的是外部節點)指針

4)紅色節點的兩個孩子都是黑色的blog

5)對於每一個節點,從該節點到其子孫葉子節點的全部路徑上包含相同數目的黑色節點get

紅黑樹節點的定義

typedef struct node
{
	int color;
	int key;
	struct node *p;//指向父節點
	struct node *left, *right;

}RBtreeNode;

與二叉搜索樹相比較,多出了2部分,本節點的顏色屬性和指向父節點是指針,之因此須要指向父節點是爲了後序操做的便利。

紅黑樹的左旋和右旋

在節點x上左旋,是以x和x的右孩子y之間的軸爲支點,旋轉結束後x成爲y的左孩子;在節點y上右旋,是以y和y的左孩子x之間的軸爲支點,旋轉結束後y是x的右孩子;如圖所示

   ,理論上與二叉搜索樹的旋轉沒有任何區別

紅黑樹的插入

假設新插入節點z,咱們將其置爲紅色,可能破壞性質2)和性質4),違反性質2)是由於新插入的點成爲了根節點,違反性質4)是由於z->p也是紅色;插入z後,從底向上逐層調整樹的結構和顏色,使其仍然知足紅黑樹的性質。既然從底層開始調整,咱們能夠只考慮違反性質4),固然在調整過程當中毫不容許出現違反其餘性質的現象。

前提z的父親是紅色

case1--z的叔叔是紅色

z的叔叔uncle_z是紅色,那z的爺爺確定是黑色,能夠把z的父親和叔叔置爲黑色,z的爺爺置爲紅色,z <- z的爺爺,而後繼續向上操做。

case2--uncle_z是黑色,且z和uncle_z不是同一方向的孩子,即z是左孩子,uncle_z是右孩子;或者z是右孩子,uncle_z是左孩子

  case2.1--z是左孩子,uncle_z是右孩子,以下面左圖所示。咱們以z的爺爺爲輸入,作一次右旋操做,同時把parent_z置爲黑色,gparent_z置爲紅色,整棵樹調整結束。

     左右分割   

  case2.2--z是右孩子,uncle_z是左孩子,如上面右圖所示。咱們,以z的爺爺爲輸入,作一次左旋操做,同時把parent_z置爲黑色,gparent_z置爲紅色,整棵樹調整結束。

 能夠看到,case2.1和case2.2是對稱的兩種狀況。

case3--uncle_z是黑色,且z和uncle_z是同一方向的孩子,即z是左孩子,uncle_z也是左孩子;或者z是右孩子,uncle_z也是右孩子

   case3.1--z和uncle_z同時是左孩子,以下面左圖所示。咱們以z的父親爲輸入,作一次右旋操做,同時把z置爲旋轉前z的父親,變成了case2.2,有木有!!

  左右分割  

  case3.2--z和uncle_z同時是右孩子,如上面右圖所示。咱們以z的父親爲輸入,作一次左旋操做,同時把z置爲旋轉前z的父親,變成了case2.1。

插入的最後,在把root的顏色置爲黑色,完事。

 紅黑樹的刪除

假設被刪除節點是z,能夠找到真正要被刪除的節點y,即若是z至多有一個孩子,y等於z;不然y是z的中序後繼,此時y至多有一個右孩子;總的說來y至多隻有一個右孩子x。若是y是紅色,刪除y後仍然可以保證紅黑樹的5個性質;若是y是黑色,可能違反性質2),4),5)。刪除y時就是把x代替了y的位置,咱們能夠給x額外增長一個黑色,使其成爲了雙黑色和紅黑色,這樣知足了性質5),但違反性質1)。

若是x是紅黑色,咱們將其置爲黑色,調整結束;

若是x是根節點,咱們將其置爲黑色,結束;

若是x是雙黑色,作必要的旋轉和塗色。

假設parent_x爲刪除節點y後x的父節點,實際上就是y的父節點,那麼parent_x必定是有兩個孩子的,由於此時y非空,且y是黑色,在不違反性質5)的前提下,y必定有兄弟。令parent_x的另外一個孩子爲w,即x的兄弟爲w

case1--w爲黑色,且w的兩個孩子都是黑色

由於w是黑色,x是雙重黑色,能夠同時去掉x和w的黑色,x的父節點增長一重黑色,不會違反性質5),而後把x賦值給x的父節點,繼續循環。以下面兩幅圖所示

    左右分割  

 case2--w是黑色,且w同一方向的孩子是紅色,即若w是左孩子,w的左孩子是紅色;若w是右孩子,w的右孩子是紅色。咱們能夠經過旋轉使x的高度減一,而且在x和x的父親之間增長一個黑色節點,最後把x置爲根節點,循環結束

  case2.1--w是黑色,w是左孩子,w的左孩子是紅色,以w的父節點爲輸入,作一次左旋,而且作一些顏色塗改

   左右分割   

   case2.2--w是黑色,w是右孩子,w的右孩子是紅色,以w的父節點爲輸入,作一次右旋,而且作一些顏色塗改

 case3--w是黑色,w同一方向的孩子是黑色,咱們能夠經過旋轉使其轉化爲case2

  case3.1--w是黑色,w是左孩子,w的左孩子是黑色,那麼右孩子必定是紅色,不然就成了case1;以w爲輸入,作一次右旋,變成了case2.1

    左右分割  

  case3.2--w是黑色,w是右孩子,w的右孩子是黑色,那麼左孩子必定是紅色,不然就成了case1;以w爲輸入,作一次左旋,變成了case2.2

  case4--w是紅色的,則w確定有黑孩子,parent_x是黑色的,以parent_x爲輸入,作一次左旋,可轉變成case1,case2,或者case3

 

 附源代碼,另附測試數據的大神博客網址:http://blog.csdn.net/v_JULY_v/article/details/6284050

//紅黑樹基本操做
#include <stdio.h>
#include <list>

#define BLACK 0
#define RED 1
typedef struct node
{
	int color;
	int key;
	struct node *p;
	struct node *left, *right;

}treeNode;

//在節點x上左旋操做,以x和x右孩子之間的軸爲支點,旋轉結束後x成爲y的左孩子
//      x                  y
//     / \   left_rotate  / \ 
//    lx  y     --->     x  ry
//       / \            / \
//      ly ry          lx ly
treeNode* left_rotate(treeNode* root, treeNode* x)
{
	treeNode *y = x->right;
	x->right = y->left;
	if(y->left != NULL)
		(y->left)->p = x;
	y->p = x->p;
	if(x->p == NULL)//x是根節點
		root = y;
	else if(x == (x->p)->left)
		(x->p)->left = y;
	else (x->p)->right = y;
	y->left = x;
	x->p = y;

	return root;
}

//在節點y上的右旋操做,以y和y的左孩子x之間的軸爲支點,旋轉結束後y是x的右孩子
//      y                  x
//     / \  right_rotate  / \ 
//    x  ry     --->     lx  y
//   / \                    / \          
//  lx rx                  rx ry
treeNode* right_rotate(treeNode* root, treeNode* y)
{
	treeNode *x = y->left;
	y->left = x->right;
	if(x->right != NULL)
		(x->right)->p = y;
	x->p = y->p;
	if(y->p == NULL)//y是根節點
		root = x;
	else if(y == (y->p)->left)
		(y->p)->left = x;
	else (y->p)->right = x;
	x->right = y;
	y->p = x;
	return root;
}

//插入新的節點後必須得調整
treeNode* rb_insert_fixup(treeNode *root, treeNode *z)
{
	treeNode *uncle_z;
	while(z->p && z->p->color == RED && z->p->p)
	{
		if (z->p->p->left == z->p)//z的父親是左孩子
		{
			uncle_z = z->p->p->right;//z的叔父是右孩子
			//case1--叔父是右孩子,紅色
			if(uncle_z && uncle_z->color == RED)
			{
				uncle_z->color = BLACK;
				z->p->color = BLACK;
				z->p->p->color = RED;
				z = z->p->p;
			}
			//case1 end
			else
			{	
				//case2--叔父是右孩子,黑色 z是右孩子
				if (z == z->p->right)//若是z是右孩子
				{
					z = z->p;
					root = left_rotate(root, z);
				}
				//case2 end 
				//case3--叔父是右孩子,黑色 z是左孩子
				z->p->color = BLACK;
				z->p->p->color = RED;
				root = right_rotate(root, z->p->p);
				//case3 end
			}
		}
		else//z的父親是右孩子
		{
			uncle_z = z->p->p->left;//z的叔父是左孩子
			//case4--叔父是左孩子,紅色
			if (uncle_z && uncle_z->color == RED)
			{
				uncle_z->color = BLACK;
				z->p->color = BLACK;
				z->p->p->color = RED;
				z = z->p->p;
			}
			//case4 end
			else 
			{	
				//case5--叔父是左孩子,黑色,z是左孩子
				if (z == z->p->left)
				{
					z = z->p;
					root = right_rotate(root, z);
				}
				//case5 end
				//case6--叔父是左孩子,黑色 z是右孩子
				z->p->color = BLACK;
				z->p->p->color = RED;
				root = left_rotate(root, z->p->p);
				//case6 end
			}
		}
	}
	root->color = BLACK;
	return root;
}

//插入新的節點
treeNode* rb_insert(treeNode *root, int value)
{
	treeNode *z = new treeNode;
	z->key = value;
	treeNode *x = root;
	treeNode *y = NULL;

	while(x != NULL)
	{
		y = x;
		if(value == x->key)
			return root;
		if(value < x->key)
			x = x->left;
		else x = x->right;
	}
	z->p = y;
	if (y == NULL)
	{
		root = z;
	}
	else
	{
		if(value < y->key)
			y->left = z;
		else
			y->right = z;
	}
	z->left = NULL;
	z->right = NULL;
	z->color = RED;
	root = rb_insert_fixup(root, z);
	return root;
}
//刪除節點x的父節點後調整, x至關於多重黑色或者紅黑色
treeNode* rb_delete_fixup(treeNode *root, treeNode *x, treeNode *parent_x)
{
	treeNode *w;//x_brother
	while(x!=root && (x == NULL ||x->color == BLACK))
	{
		//x的父親parent_x必定有兩個孩子
		if(x == parent_x->left)
		{
			w = parent_x->right;//x的右兄弟w
			//case1--x的兄弟w爲紅色,則w確定有黑孩子,左旋,轉換成case2,case3....
			if(w->color == RED)
			{
				w->color = BLACK;
				parent_x->color = RED;
				root = left_rotate(root, parent_x);
				w = parent_x->right; //w從新置爲parent_x的右孩子
			}
			//case1 end
			//case2--w爲黑色,w的兩個孩子(若是有)也是黑色
			if((w->left == NULL || w->left->color == BLACK) && (w->right == NULL || w->right->color == BLACK))
			{
				w->color = RED;
				x = parent_x;
				parent_x = parent_x->p;
			}
			//case2 end	
			else
			{	//case3--w的右孩子是黑色,左孩子是紅色
				if(w->right == NULL || w->right->color == BLACK)
				{
					if(w->left != NULL)//若是w有左孩子,必定是紅色
						w->left->color = BLACK;
					w->color = RED;
					root = right_rotate(root, w);
					w = parent_x->right;
				}
				//case3 end
				//case4 w的右孩子是紅色
				if (w->right != NULL)//若是w有右孩子,必定是紅色
					w->right->color = BLACK;
				w->color = parent_x->color;
				parent_x->color = BLACK;	
				root = left_rotate(root, parent_x);
				x = root;
			}
		}
		else//與上面對稱
		{
			w = parent_x->left;//x的左兄弟w
			//case5--x的兄弟w爲紅色,則w確定有黑孩子,右旋,轉換成case2,case3....
			if(w->color == RED)
			{
				w->color = BLACK;
				parent_x->color = RED;
				root = right_rotate(root, parent_x);
				w = parent_x->left;
			}
			//case5 end
			//case6--w爲黑色,w的兩個孩子(若是有)也是黑色
			if((w->left == NULL || w->left->color == BLACK) && (w->right == NULL || w->right->color == BLACK))
			{
				w->color = RED;
				x = parent_x;
				parent_x = parent_x->p;
			}
			//case6 end
			else
			{
			//case7--w的左孩子是黑色,右孩子是紅色
				if(w->left == NULL || w->left->color == BLACK)
				{
					if(w->right != NULL)
						w->right->color = BLACK;
					w->color = RED;
					root = left_rotate(root, w);
					w = parent_x->left;
				}
				//case7 end
				//case8 w的左孩子是紅色
				if (w->left->color == RED)
					w->left->color = BLACK;
				w->color = parent_x->color;
				parent_x->color = BLACK;	
				root = right_rotate(root, parent_x);
				x = root;
				//case8 end
			}
		}
	}

	if(x != NULL)
		x->color = BLACK;
	return root;
}

treeNode* rb_delete(treeNode *root, int value)
{
	treeNode *z, *y, *x;
	z = root;
	while(z != NULL)
	{
		if(z->key == value)
			break;
		if(z->key < value)
			z = z->right;
		else
			z = z->left;
	}
	if(z == NULL) //木有找到要刪除的節點
		return root;
	y = z;
	if(y->left!=NULL && y->right!=NULL)//兩個孩子的狀況,y是z的中序後繼
	{
		y = y->right;
		while (y->left != NULL)
		{
			y = y->left;
		}
		z->key = y->key;
	}
	//x是y惟一的孩子或者NULL
	if(y->left != NULL)
		x = y->left;
	else x = y->right;
	
	if(x)
		x->p = y->p;
	if(y->p == NULL)//y是根節點
		root = x;
	else if(y == y->p->left)
		y->p->left = x;
	else y->p->right = x;
	
	if(y->color==BLACK)
		root = rb_delete_fixup(root, x, y->p);
	delete y;
	return root;
}

treeNode* rb_find(treeNode *root, int value)
{
	treeNode *x = root;
	int h = 0;
	while (x != NULL)
	{
		if (x->key == value)
		{
			if(x->color == RED)
				printf("RED ");
			else
				printf("BLACK ");
			printf("從0層計數,%d在第%d層\n", value, h);
			return x;
		}
		if (x->key < value)
			x = x->right;
		else x = x->left;
		h++;
	}
	return NULL;
}

void midOrder(treeNode *root)
{
	if(root == NULL)
		return;
	midOrder(root->left);
	printf("%d-", root->key);
	if(root->color == RED)
		printf("RED ");
	else
		printf("BLACK ");
	midOrder(root->right);
}

void preOrder(treeNode *root)
{
	if(root == NULL)
		return;
	printf("%d-", root->key);
	if(root->color == RED)
		printf("RED ");
	else
		printf("BLACK ");
	preOrder(root->left);
	preOrder(root->right);
}


int main()
{
	int select, a;
	treeNode *root = NULL;
	do 
	{
		printf("請輸入選擇 1--插入,2--查詢, 3--刪除, 4--輸出中序和前序,其餘跳出\n");
		scanf("%d", &select);
		if (select == 1)
		{
			printf("輸入將要插入的值, -1表示結束\n");
			while(scanf("%d", &a), a != -1)
			{
				root = rb_insert(root, a);
			}
		}
		else if (select == 2)
		{
			printf("輸入將要查詢的值, -1表示退出查詢\n");
			while(scanf("%d", &a), a != -1)
			{
				if(rb_find(root, a) != NULL)
				{
					//printf("找到了\n");
				}
				else
					printf("木有找到\n");
			}
		}
		else if(select == 3)
		{
			printf("輸入將要刪除的值, -1表示退出刪除\n");
			while(scanf("%d", &a), a != -1)
			{
				root = rb_delete(root, a);
			}
		}
		else if(select == 4)
		{
			printf("中序是:\n");
			midOrder(root);
			printf("\n前序是:\n");
			preOrder(root);
			printf("\n");
		}
		else break;

	} while (true);
	

	getchar();
	return 0;
}

運行結果,據小雨本身測試沒有錯誤,歡迎你們前來拍磚

相關文章
相關標籤/搜索