红黑树的解析与实现

红黑树

   上一篇中讲到,一棵高度为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;
}


   



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值