紅黑樹

轉自 http://blog.csdn.net/Hackbuteer1?viewmode=contents and http://blog.csdn.net/v_JULY_v/article/details/6114226


hackbutter1

一、紅黑樹概述

     紅黑樹和我們以前學過的AVL樹類似,都是在進行插入和刪除操作時通過特定操作保持二叉查找樹的平衡,從而獲得較高的查找性能。不過自從紅黑樹出來 後,AVL樹就被放到了博物館裏,據說是紅黑樹有更好的效率,更高的統計性能。這一點在我們瞭解了紅黑樹的實現原理後,就會有更加深切的體會。
     紅黑樹和AVL樹的區別在於它使用顏色來標識結點的高度,它所追求的是局部平衡而不是AVL樹中的非常嚴格的平衡。學過數據結構的人應該都已經領教過AVL樹的複雜,但AVL樹的複雜比起紅黑樹來說簡直是小巫見大巫,紅黑樹纔是真正的變態級數據結構。
     由於STL中的關聯式容器默認的底層實現都是紅黑樹,因此紅黑樹對於後續學習STL源碼還是很重要的,有必要掌握紅黑樹的實現原理和源碼實現。
     紅黑樹是AVL樹的變種,紅黑樹通過一些着色法則確保沒有一條路徑會比其它路徑長出兩倍,因而達到接近平衡的目的。所謂紅黑樹,不僅是一個二叉搜索樹,而且必須滿足一下規則:
     1、每個節點不是紅色就是黑色。
     2
、根節點爲黑色。
     3
、如果節點爲紅色,其子節點必須爲黑色。
     4
、任意一個節點到到NULL(樹尾端)的任何路徑,所含之黑色節點數必須相同。
上面的這些約束保證了這個樹大致上是平衡的,這也決定了紅黑樹的插入、刪除、查詢等操作是比較快速的。 根據規則4,新增節點必須爲紅色;根據規則3,新增節點之父節點必須爲黑色。當新增節點根據二叉搜索樹的規則到達其插入點時,卻未能符合上述條件時,就必須調整顏色並旋轉樹形,如下圖:

假設我們爲上圖分別插入節點3、8、35、75,根據二叉搜索樹的規則,插入這四個節點後,我們會發現它們都破壞了紅黑樹的規則,因此我們必須調整樹形,也就是旋轉樹形並改變節點的顏色。

二、紅黑樹上結點的插入

      在討論紅黑樹的插入操作之前必須要明白,任何一個即將插入的新結點的初始顏色都爲紅色。這一點很容易理解,因爲插入黑點會增加某條路徑上黑結點的數目,從而導致整棵樹黑高度的不平衡。但如果新結點的父結點爲紅色時(如下圖所示),將會違反紅黑樹的性質:一條路徑上不能出現相鄰的兩個紅色結點。這時就需要通 過一系列操作來使紅黑樹保持平衡。

      爲了清楚地表示插入操作以下在結點中使用“新”字表示一個新插入的結點;使用“父”字表示新插入點的父結點;使用“叔”字表示“父”結點的兄弟結點;使用“祖”字表示“父”結點的父結點。插入操作分爲以下幾種情況:
1、黑父  (直接插)
     如下圖所示,如果新節點的父結點爲黑色結點,那麼插入一個紅點將不會影響紅黑樹的平衡,此時插入操作完成。紅黑樹比AVL樹優秀的地方之一在於黑父的情況比較常見,從而使紅黑樹需要旋轉的機率相對AVL樹來說會少一些。

2、紅父(紅父)
     如果新節點的父結點爲紅色,這時就需要進行一系列操作以保證整棵樹紅黑性質。如下圖所示,由於父結點爲紅色,此時可以判定,祖父結點必定爲黑色。這時需要 根據叔父結點的顏色來決定做什麼樣的操作。青色結點表示顏色未知。由於有可能需要根結點到新點的路徑上進行多次旋轉操作,而每次進行不平衡判斷的起始點 (我們可將其視爲新點)都不一樣。所以我們在此使用一個藍色箭頭指向這個起始點,並稱之爲判定點。

2.1 紅叔(紅父+紅叔變黑祖父變紅(當祖父是根的時候,必須爲黑.))
當叔父結點爲紅色時,如下圖所示,無需進行旋轉操作,只要將父和叔結點變爲黑色,將祖父結點變爲紅色即可。但由於祖父結點的父結點有可能爲紅色,從而違反紅黑樹性質。此時必須將祖父結點作爲新的判定點繼續向上(迭代)進行平衡操作。

需要注意的是,無論“父節點”在“叔節點”的左邊還是右邊,無論“新節點”是“父節點”的左孩子還是右孩子,它們的操作都是完全一樣的(其實這種情況包括4種,只需調整顏色,不需要旋轉樹形)。
2.2 黑叔 (紅父黑叔各種變)
當叔父結點爲黑色時,需要進行旋轉,以下圖示了所有的旋轉可能:
Case 1:

Case 2:

Case 3:

Case4:

     可以觀察到,當旋轉完成後,新的旋轉根全部爲黑色,此時不需要再向上回溯進行平衡操作,插入操作完成。需要注意,上面四張圖的“叔”、“1”、“2”、“3”結點有可能爲黑哨兵結點。
      其實紅黑樹的插入操作不是很難,甚至比AVL樹的插入操作還更簡單些。

july

十、紅黑樹刪除的4種情況

情況1:x的兄弟w是紅色的。

情況2:x的兄弟w是黑色的,且w的倆個孩子都是黑色的。

情況3:x的兄弟w是黑色的,w的左孩子是紅色,w的右孩子是黑色。

情況4:x的兄弟w是黑色的,且w的右孩子時紅色的。

情況5: x爲紅色 直接刪除

操作流程圖:

ok,簡單分析下,紅黑樹刪除的4種情況:

針對情況1x的兄弟w是紅色的。

上圖重要看僞代碼

針對情況2x的兄弟w是黑色的,且w的倆個孩子都是黑色的

針對情況3x的兄弟w是黑色的,w的左孩子是紅色,w的右孩子是黑色。

針對情況4x的兄弟w是黑色的,且w的右孩子時紅色的

code: july

#include <iostream>
typedef int key_t;
typedef int data_t;
typedef enum color_t
{
	RED=0,
	BLACK=1
}color_t;
typedef struct rb_node_t
{
	struct rb_node_t *left,*right,*parent;
	key_t key;
	data_t data;
	color_t color;
}rb_node_t;
rb_node_t* rb_insert(key_t key,data_t data,rb_node_t* root);
rb_node_t* rb_search(key_t key,rb_node_t* root);
rb_node_t* rb_insert_rebalance(rb_node_t* node,rb_node_t* root);
rb_node_t* rb_erase(key_t key,rb_node_t *root);
rb_node_t* rb_earse_rebalance(rb_node_t* node,rb_node_t* parent,rb_node_t* root);
static rb_node_t* rb_new_node(key_t key,data_t data)
{
	rb_node_t* node=(rb_node_t*)malloc(sizeof(struct rb_node_t));
	if(!node)
	{
		printf("malloc error!\n");
		exit(-1);
	}
	node->key=key;
	node->data=data;
	return node;
}
//左旋
static rb_node_t* rb_rotate_left(rb_node_t* node,rb_node_t* root)
{
	rb_node_t* right=node->right;
	if((node->right=right->left))
	{
		right->left->parent=node;
	}
	right->left=node;
	if((right->parent=node->parent))
	{
		if(node==node->parent->right)
			node->parent->right=right;
		else
			node->parent->left=right;
	}
	else
	{//根
		root=right;
	}
	node->parent=right;
	return root;
}
//右旋
static rb_node_t* rb_rotate_right(rb_node_t* node,rb_node_t* root)
{
	rb_node_t* left=node->left;
	if((node->left=left->right))
		left->right->parent=node;
	if((left->parent=node->parent))
	{//node非根
		if(node==node->parent->right)
			node->parent->right=left;
		else
			node->parent->left=left;
	}
	else
		root=left;
	node->parent=left;
	return root;
}
static rb_node_t* rb_search_auxiliary(key_t key,rb_node_t* root,rb_node_t** save)
{
	rb_node_t *node=root,*parent=NULL;
	int ret;
	while(node)
	{
		parent=node;
		ret=node->key-key;
		if(0<ret)
			node=node->left;
		else if(0>ret)
			node=node->right;
		else
			return node;
	}
	if(save)
		*save=parent;
	return NULL;//當沒有找到的時候返回該插入節點的父節點
}
rb_node_t* rb_search(key_t key,rb_node_t* root)
{
	return rb_search_auxiliary(key,root,NULL);
}
//紅黑樹的插入
rb_node_t* rb_insert(key_t key,data_t data,rb_node_t* root)
{
	rb_node_t* parent=NULL;
	rb_node_t* node;
	parent=NULL;
	if((node=rb_search_auxiliary(key,root,&parent)))//如果都已經有了就不需要在插入了
		return root;
	//parent返回的是該插入節點的位置
	node=rb_new_node(key,data);
	node->parent=parent;
	node->left=node->right=NULL;
	node->color=RED;
	if(parent)
	{
		if(parent->key>key)
			parent->left=node;
		else
			parent->right=node;
	}
	else
		root=node;
	return rb_insert_rebalance(node,root);
}
//紅黑樹的調整
//z表示當前節點,p[z]表示父母,p[p[z]]表示祖父,y表示叔叔
static rb_node_t* rb_insert_rebalance(rb_node_t* node,rb_node_t* root)
{
	rb_node_t* parent;
	rb_node_t* gparent;
	rb_node_t* uncle;
	rb_node_t* tmp;
	while((parent=node->parent)&&parent->color==RED)
	{//parent爲node的父母,當父母顏色爲紅色的時候需要調整,否則不需要調整
		gparent=parent->parent;//祖父
		if(parent==gparent->left)//當祖父的左孩子爲父親是
		{
			uncle=gparent->right;
			if(uncle&&uncle->color==RED)
			{//紅父紅叔
				uncle->color=BLACK;
				parent->color=BLACK;
				gparent->color=RED;
				node=gparent;//將祖父作爲新增節點卓爲紅色
			}
			else
			{//紅父黑叔右孩子
				if(parent->right==node)
				{
					root=rb_rotate_left(parent,root);
					tmp=parent;
					parent=node;
					node=tmp;//交換parent與node
				}
				parent->color=BLACK;//
				gparent->color=RED;//祖父節點變爲紅色的
				root=rb_rotate_right(gparent,root);
			}
		}
		else
		{
			uncle=gparent->left;//祖父的做孩子作爲叔叔節點
			if(uncle&&uncle->color==RED)
			{//叔叔父親全是紅色的
				uncle->color=BLACK;
				parent->color=BLACK;
				gparent->color=RED;
				node=gparent;
			}
			else
			{//黑叔叔
				if(parent->left==node)
				{//左孩子黑叔叔
					root=rb_rotate_right(parent,root);
					tmp=parent;
					parent=node;
					node=tmp;
				}
				//	右邊孩子,黑叔叔
				parent->color=BLACK;
				gparent->color=RED;
				root=rb_rotate_left(gparent,root);
			}
		}
	}
	root->color=BLACK;
	return root;
}
//紅黑樹的刪除操作
static rb_node_t* rb_erase(key_t key,rb_node_t *root)
{
	rb_node_t *child,*parent,*old,*left,*node;
	color_t color;
	if(!(node=rb_search_auxiliary(key,root,NULL)))
	{//如果就沒有找到直接返回
		printf("key %d is not exist!\n");
		return root;
	}
	old=node;
	if(node->left&&node->right)
	{//node有左右孩子,並且需要刪除node節點,用node的直接後繼替換之
		node=node->right;//找old節點的直接後繼
		while((left=node->left)!=NULL)
			node=left;
		child=node->right;
		parent=node->parent;
		color=node->color;
		if(child)
		{
			child->parent=parent;
		}
		if(parent)
		{
			if(parent->left==node)
				parent->left=child;
			else
				parent->right=child;
		}
		else
		{
			root=child;
		}
		if(node->parent==old)
		{
			parent=node;
		}
		node->parent=old->parent;
		node->color=old->color;
		node->right=old->right;
		node->left=old->left;
		if(old->parent)
		{
			if(old->parent->left==old)
				old->parent->left=node;
			else
				old->parent->right=node;
		}
		else
			root=node;
		old->left->parent=node;
		if(old->right)
			old->right->parent=node;
	}
	else
	{
		if(!node->left)
		{//node的左孩子爲空
			child=node->right;
		}
		else
			child=node->left;
		parent=node->parent;
		color=node->color;
		if(child)
			child->parent=parent;
		if(parent)
		{
			if(parent->left==node)
				parent->left=child;
			else
				parent->right=child;
		}
		else
			root=child;
	}
	free(old);
	if(color==BLACK)
	{//刪除黑色節點需要調整樹形結構
		root=rb_earse_rebalance(child,parent,root);
	}
	return root;
}
//紅黑樹刪除的種情況
//修復之
static rb_node_t* rb_earse_rebalance(rb_node_t* node,rb_node_t* parent,rb_node_t* root)
{
	rb_node_t *other,*o_left,*o_right;
	while((!node||node->color==BLACK)&&node!=root)
	{//node爲空或者node的節點爲黑色
		if(parent->left==node)
		{
			other=parent->right;//node的兄弟節點
			if(other->color==RED)//情況:node的兄弟節點爲紅色
			{
				other->color=BLACK;
				parent->color=RED;
				root=rb_rotate_left(parent,root);
				other=parent->right;//轉化爲情況,3,4
			}
			if((!other->left||other->left->color==BLACK)&&(!other->right||other->right->color==BLACK))
			{//情況node的兄弟是黑色的兄弟的兩個兒子也是黑色的
				other->color=RED;
				node=parent;
				parent=node->parent;
			}
			else
			{
				if(!other->right||other->right->color==BLACK)
				{//情況兄弟是黑色的,右孩子爲黑色的
					if((o_left=other->left))
						o_left->color=BLACK;
					other->color=RED;
					root=rb_rotate_right(other,root);
					other=parent->right;//轉變爲情況四
				}
				//情況:x的兄弟w是黑色的,但是兄弟的右孩子是紅色的
				other->color=parent->color;
				parent->color=BLACK;
				if(other->right)//
				{
					other->right->color=BLACK;
				}
				root=rb_rotate_left(parent,root);
				node=root;
				break;
			}
		}
		else
		{
			 other = parent->left;  
            if (other->color == RED)  
            {  
                other->color = BLACK;  
                parent->color = RED;  
                root = rb_rotate_right(parent, root);  
                other = parent->left;  
            }  
            if ((!other->left || other->left->color == BLACK) &&  
                (!other->right || other->right->color == BLACK))  
            {  
                other->color = RED;  
                node = parent;  
                parent = node->parent;  
            }  
            else  
            {  
                if (!other->left || other->left->color == BLACK)  
                {  
                    if ((o_right = other->right))  
                    {  
                        o_right->color = BLACK;  
                    }  
                    other->color = RED;  
                    root = rb_rotate_left(other, root);  
                    other = parent->left;  
                }  
                other->color = parent->color;  
                parent->color = BLACK;  
                if (other->left)  
                {  
                    other->left->color = BLACK;  
                }  
                root = rb_rotate_right(parent, root);  
                node = root;  
                break;  
            }  
		}
	}
	if(node)
		node->color=BLACK;//根節點必須爲黑色的
	return root;
}
int main()
{
	int i,count=100;
	key_t key;
	rb_node_t* root=NULL,*node=NULL;
	srand(NULL);
	for(i=1;i<count;i++)
	{
		key=rand()%count;
		if((root=rb_insert(key,i,root)))
			printf("[i=%d] insert key %d success\n",i,key);
		else
		{
			printf("[i=%d] insert key %d error!\n",i,key);
		}
		if((node=rb_search(key,root)))
			printf("[i=%d] search key %d success!\n",i,key);
		if(!(i%10))
		{
			if((root=rb_erase(key,root)))
				printf("[i=%d] erase key %d success\n",i,key);
			else
				printf("error");
		}
	}
	return 0;
}



發佈了27 篇原創文章 · 獲贊 15 · 訪問量 28萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章