紅黑樹的解析與實現

紅黑樹

   上一篇中講到,一棵高度爲h的二叉搜索樹,它所進行的操作都可以在O(h)時間內完成。因此搜索樹的高度較低時,可以較快的完成。但是,如果樹的高度較高時,這些集合操作可能並不比鏈表上快。如何實現一種搜索樹的結構,使得其任何一種基本操作都可以在O(lgn)的時間內完成呢?

    紅黑樹,就是這種“平衡”搜索樹中的一種。可以保證最壞的情況下,基於動態集合操作的時間複雜度爲O(lgn)。

    紅黑樹,也是一種二叉搜索樹,也支持search, predecessor, successor, minimum, maximum, insert 和 delete等操作,但是卻有着和普通二叉搜索樹不同的性質,於是也導致它的某些操作的複雜性,但是以code的複雜度換取時間的複雜度,不亦樂乎。

   下面開始一起走入紅黑樹吧:

    1、性質:

     (1)每個結點要麼是紅色,要麼是黑色;

     (2)根結點是黑色的;

     (3)每個葉子結點是黑色的。

     (4)如果一個結點是紅色的,則它的兩個子結點都是黑色的。

     (5)對於每個結點,從該結點到其所有後代葉結點的簡單路徑上,均包含相同數目的黑色結點。

                    

    


     2、準備知識  --->旋轉
     搜索樹在insert 和 delete的時候可能違反它的一些性質,因此必須改變某些結點的顏色和指針,以維持它的性質不變。指針的修改便是通過“旋轉"來完成的。
     (1)左轉
 
Left_Rotate( T, x)

y = x.right
x.right = y.left
if y.left != T.NIL
   y.left.p = x
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

右轉可以對稱來實現。

3、插入
  我們可以依據普通二叉搜索樹的insert過程插入一個新的結點z, 並將其着爲紅色(爲了儘量避免對紅黑樹進行調整)。看看插入新的結點z後對它的性質有什麼影響:
   (1)如果插入的是根結點,則違背了性質2,簡單地將其着爲黑色即可。 
   (2)如果插入結點後,它的父結點是紅色,則違背了性質4,針對這種情況的調整方案分析如下:
  
   

   4、刪除
        刪除一個結點,延用普通二叉搜索樹的刪除方案:
        若要刪除的結點,它有非雙空子結點,則可以直接刪除該結點,讓它的唯一子結點指向它。若子結點都爲空,則用NULL取代原來的結點。若子結點均非空,則用它的後繼結點的值取代它的值,然後以同樣的方式刪除它的後繼結點即可。(所以,要刪除的結點是z,那麼實際刪除的是它的後繼結點y,而這麼一個刪除的過程是一個遞歸過程,慢慢體會吧)
        但是在刪除節點後,原來紅黑樹的性質可能被改變,如果被刪除的結點是紅色的結點,那麼一切OK。如果被刪除 結點是黑色的話,可能就會破壞它的某些性質:
        (1)如果要刪除的結點不是唯一的樹結點,那麼從它爲根結點,到各個支路的黑高可能會受到影響,從而破壞性質5
        (2)如果被刪除的結點是根結點,而它的唯一子結點是紅色,則破壞了性質1.
   如下,是我在紙張畫的分析過程,直接貼上來:
  

  
至此已分析完畢。貼上實現的代碼:
// RB_Tree_.cpp : 定義控制檯應用程序的入口點。
//

#include "stdafx.h"
#include <iostream>

using namespace std;

enum Color
{
	Black,
	Red
};

typedef struct RB_Node
{
	Color color;
	int key;
	struct RB_Node *left;
	struct RB_Node *right;
	struct RB_Node *p;
}RB_Node, *p_RB_Node;

RB_Node *T_NULL = NULL;
RB_Node * FindNode(RB_Node *root, int k)
{
	while(root!=NULL && k!=root->key)
	{
		if(k<root->key)
			root = root->left;
		else 
			root = root->right;

	}
	if(root == NULL)
		return NULL;
	else 
		return root;

}

RB_Node * RB_Tree_Minimum(RB_Node *x)
{
	RB_Node *y = T_NULL;
	while(x!=T_NULL)
	{
		y = x;
		x = x->left;
	}
	return y;
}

RB_Node * Succssor(RB_Node *root, RB_Node *x)
{
	if(x->right !=T_NULL)
		return RB_Tree_Minimum(x->right);
	RB_Node *y = x->p;
	while(y!=T_NULL && x == y->right)
	{
		x = y;
		y = y->p;
	}
	return y;
}

void Left_Rotate(p_RB_Node *root, RB_Node *x)
{
	RB_Node *y = x->right;
	x->right = y->left;
	if(y->left!=T_NULL)
		y->left->p = x;
	y->p = x->p;
	if(x->p == T_NULL)
		*root = y;
	if(x == x->p->left)
		x->p->left = y;
	else
		x->p->right = y;
	y->left = x;
	x->p = y;
}

void Right_Rotate(p_RB_Node *root, RB_Node *x)
{
	RB_Node *y = x->left;
	x->left = y->right;
	if(y->right != T_NULL)
		y->right->p = x;
	y->p = x->p;
	if(x->p ==T_NULL)
		*root = y;
	if(x == x->p->left)
		x->p->left = y;
	else
		x->p->right = y;

	y->right = x;
	x->p = y;
}

void RB_Insert_FixUP(p_RB_Node *root, RB_Node *z)
{
	/*
	1.z是要插入的節點,p是其父節點,G是祖父節點,W是叔父節點;存在兩種情況:p = G.left and p = G.right
	注意破壞的是什麼性質,去fix的是什麼性質。
	*/
	while(z->p->color == Red)
	{
		RB_Node *p = z->p;
		RB_Node *G = z->p->p;
		RB_Node *W = T_NULL;
		if(p == G->left)
		{
			W = G->right;
			//case 1:z的父節點p和叔父節點W均是紅色,那麼G一定是黑色,則將p和w均着爲黑色,將G着爲紅色
			if( W->color ==Red)
			{
				p->color = Black;
				W->color = Black;
				G->color = Red;
				z = G;
			}
			//case 2:w是黑色,且z是其父節點的右孩子,那麼將p作爲當前節點,並以它爲支點,左旋
			else if(z == p->right)
			{
				z = p;
				Left_Rotate(root,p);
			}
			//case 3:w是黑色,z是p的左孩子,將p,G的顏色交換,然後以G爲支點,右旋。
			else
			{
				p->color = Black;
				G->color = Red;
				Right_Rotate(root,G);
			}
		}

		else
		{
			W = G->left;
			if( W->color ==Red)
			{
				p->color = Black;
				W->color = Black;
				G->color = Red;
				z = G;
			}
			else if(z == p->left)
			{
				z = p;
				Right_Rotate(root,p);
			}
			else
			{
				p->color = Black;
				G->color = Black;
				Left_Rotate(root,G);
			}
		}
	}
	(*root)->color = Black;
}

void RB_Insert(p_RB_Node *root, int k)
{
	RB_Node *x = *root;
	RB_Node *z = new RB_Node;
	RB_Node *temp = T_NULL;
	z->color = Red;
	z->key = k;
	z->left = z->right = z->p = T_NULL;
	while(x!=T_NULL)
	{
		temp = x;
		if(k<x->key)
			x = x->left;
		else 
			x = x->right;
	}
	z->p = temp;
	if(temp == T_NULL)
		*root = z;
	else if(k<temp->key)
		z->p->left = z;
	else
		z->p->right = z;
	if(z->p==T_NULL)
		z->color = Black;
	else
		RB_Insert_FixUP(root,z);
	//delete z;
}

void RB_Delete_Fixup(p_RB_Node *root, RB_Node *x)
{
	while(x!=(*root)&& x->color != Red)
	{
		RB_Node *p = x->p;
		RB_Node *w = T_NULL; //w爲x的兄弟節點
		if(x == p->left)
		{
			w = p->right;
			//case 1: 兄弟w 爲紅色,那麼p一定是黑色,p與w換顏色,然後以p 爲支點左旋
			if(w->color == Red)
			{
				w->color = Black;
				p->color = Red;
				Left_Rotate(root,p);
			}
			//case 2: 兄弟爲黑色,且兄弟的兩孩子也均爲黑色,分別提取x和w的一層黑,加到p上,以p作爲新的x 結點,繼續遞歸
			if(w->left->color == Black && w->right->color == Black)
			{
				w->color = Red;
				p->color = Black;
				x = p;
				
			}
			//case 3:兄弟爲黑色,左孩子爲紅色,右孩子爲黑色。
			//w與w.left交換顏色,然後以w爲支點右旋---->到達case 4
			else if(w->left->color == Red && w->right->color == Black)
			{
				w->left->color = Black;
				w->color = Red;
				Right_Rotate(root,w);
			}
			//case 4:兄弟爲黑色,右孩子爲紅色,左孩子任意。
			//w着爲p的顏色,p着爲黑色,兄弟右子結點染黑。然後以p爲支點左旋,最終恢復紅黑樹的性質
			else if(w->right->color == Red)
			{
				w->color = p->color;
				p->color = Black;
				w->right->color = Black;
				Left_Rotate(root,p);
				x =(*root);
			}
		}
		else
		{
			w = p->left;
			if(w->color == Red)
			{
				w->color = Black;
				p->color = Red;
				Right_Rotate(root,p);
			}
			if(w->left->color == Black && w->right->color == Black)
			{
				w->color = Red;
				p->color = Black;
				x = p;

			}
			else if(w->right->color == Red && w->left->color == Black)
			{
				w->right->color = Black;
				w->color = Red;
				Left_Rotate(root,w);
			}
			else if(w->left->color == Red)
			{
				w->color = p->color;
				p->color = Black;
				w->left->color = Black;
				Right_Rotate(root,p);
				x = (*root);
			}
		}
	}
	(*root)->color = Black;
}

void RB_Delete_Node(p_RB_Node *root, int k)
{
	//z是要刪除的節點,y是實際上被刪除的節點,x是y節點的孩子
	RB_Node *z = FindNode(*root,k);
	RB_Node *y = T_NULL;
	RB_Node *x = T_NULL;
	if(z->left == T_NULL && z->right == T_NULL)
		y = z;
	else
		y = Succssor(*root,z);
	if(y->left !=T_NULL)
		x = y->left;
	else 
		x = y->right;
	x->p = y->p;
	if(x->p == T_NULL)
		*root = x;
	else if(y == y->p->left)
		y->p->left = x;
	else
		y->p->right = x;

	if(y!=z)
		z->key = y->key;

	if(y->color == Black)
		RB_Delete_Fixup(root,x);
}

void RB_Tree_Inoder(RB_Node *root)
{
	if(root!=T_NULL)
	{
		RB_Tree_Inoder(root->left);
		printf("%d  ,",root->key);
		RB_Tree_Inoder(root->right);
	}
}
void RB_Tree_Create(p_RB_Node *root)
{
	int a[] = {12,1,9,2,0,11,7,19,4,15,18,5,14,13,10,16,6,3,8,17};
	for(int i = 0;i<20;i++)
	{
		RB_Insert(root,a[i]);
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	T_NULL = new RB_Node;
	T_NULL->color = Black;
	T_NULL->left = NULL;
	T_NULL->right = NULL;
	T_NULL->p = NULL;
	T_NULL->key = 0;
	RB_Node *root = T_NULL;
	RB_Tree_Create(&root);
	RB_Tree_Inoder(root);
	cout<<endl;
	RB_Delete_Node(&root,12);
	RB_Tree_Inoder(root);
	cout<<endl;
	RB_Delete_Node(&root,1);
	RB_Tree_Inoder(root);
	cout<<endl;
	RB_Delete_Node(&root,9);
	RB_Tree_Inoder(root);
	cout<<endl;
	return 0;
}


   



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