算法导论十三章复习

/*红黑树:一种二叉查找树,某种程度上也是一种平衡树,其中平衡树还有avl树。
红黑树:对于红黑树的而言,首先是一个二叉查找树,此外每一个节点包含的还有一项技术指标就是
每个节点还有一个表示该节点颜色的项目,节点要么是红色要么是黑色,为了限制红黑树的平衡型,通过
使各个分支上的红黑的着色方式达到一种近似的平衡,所以红黑树算是一种平衡树,类似的平衡树还有:avl树,treap树;
红黑树的特性:(1)每一个节点颜色:红或者黑;
(2)根节点是黑色的;
(3)一个节点为红,子节点为黑;
(4)每一个叶子节点(算法导论里面的意思为非含有键值节点)为黑;
(5)从某一节点起到叶子节点为止,各个分支的黑色的节点个数一样。(黑色的个数也叫黑高度,一个树的黑高度就是从树的根节点开始到叶子节点。			
红黑树的基本动态操作是O(lgn)
*/
#include <iostream>
#define RED 1
#define BLACK 0
using namespace std;
struct node
{
	int key;
	node* p;
	node* left;
	node* right;
	bool color;
	node(){
		key=0;
		p=NULL;
		left=NULL;
		right=NULL;
		color=RED;
	}
	node(int k):key(k){
		p=NULL;
		left=NULL;
		right=NULL;
		color=RED;
	}
	node& operator=(node& other)
	{	
		if(this!=&other)
		{
			this->key=other.key;
			this->p=other.p;
			this->color=other.color;
			this->left=other.left;
			this->right=other.right;
		}
		return *this;
	}
};
struct rbtree
{
	node* root;//根
	node* nil;//哨兵
	rbtree()
	{
		int k;
		cin>>k;
		root=new node(k);
		root->color=BLACK;
		nil=new node(100000);
		nil->color=BLACK;
		root->p=nil;//一开始根节点就指向该值
		root->left=nil;//最初的时候如此的进行设计。
		root->right=nil;
	}
	void letf_rotate(node*);//o(1)
	void right_rotate(node*);//o(1)
	void rb_insert_fixup(node*);
	void rb_insert(node*);//o(lgn)
	void rb_delete_fixup(node*);
	node* rb_delete(node*);//o(lgn)
	node* rb_successor(node*);//辅助
	node* rb_min(node*);//辅助
};

void rbtree::letf_rotate(node* px)
{
	node* py=px->right;
	if(py!=nil)
	{
		px->right=py->left;
		if(py->left!=nil)
			py->left->p=px;
		py->p=px->p;
		if(px==nil)
			root=py;
		if(px==px->p->left)
			px->p->left=py;
		else
			px->p->right=py;
		py->left=px;
		px->p=py;
	}
	else
	{
		cerr<<"left of px is null "<<endl;
		return ;//一般情况下函数返回值为void 可以如此;
	}
	cout<<"finished! "<<endl;
}

void rbtree::right_rotate(node* px)//13.2-1
{
	node* py=px->left;
	if(py!=nil)
	{
		px->left=py->right;
		if(py->right!=nil)
			py->right->p=px;
		py->p=px->p;
		if(px->p==nil)
			root=py;
		if(px==px->p->left)
			px->p->left=py;
		else
			px->p->right=py; 
		py->right=px;
		px->p=py;
	}else
	{
		cerr<<"right of px is nil "<<endl;
		return ;
	}
	cout<<"finishede!"<<endl;
}
/************rbtree_insert()************************/
void rbtree::rb_insert_fixup(node* pz)
{
	  node* py=new node();
	  while(pz->p->color==RED)//对于红黑树,插入一个点为红,只要保证最后的树是满足红黑树的五个性质即可,插入的是红点,黑高度不影响,那么插入
	  {//红点,会造成的影响是:如果插入的点成为根节点的话;以及插入节点的父节点为红。这里以插入节点的父节点为红做判断。
		if(pz->p==pz->p->p->left)
		{
			py=pz->p->p->right;//py为pz的叔叔
			if(py->color==RED)//情况1:叔叔为红的时候
			{
				pz->p->color=BLACK;
				py->color=BLACK;
				pz->p->p->color=RED;//修改颜色使父辈变黑,爷爷变红,这样节点黑高度不受影响,原因是pz为红,pz父辈为红,那么对应必定有祖父辈,插入前的rbtree为
				//正常的,父辈不会为根,那么存在祖父辈,如果祖父辈为红,父辈怎么会为红呢?祖父辈必定为黑以保证插入前的rbtree是正常。
				pz=pz->p->p;
			}else 
			{
				if(pz==pz->p->right)//情况2:叔叔为黑,且pz是右孩子
				{
					pz=pz->p;
					letf_rotate(pz);
				}
				pz->p->color=BLACK;//情况3:叔叔为黑,pz是左孩子
				pz->p->p->color=RED;
				right_rotate(pz->p->p);
			}
		}else if(pz->p==pz->p->p->right)
		{
			py=pz->p->p->left;//叔叔
			if(py->color==RED)
			{
				pz->p->color=BLACK;
				py->color=BLACK;
				pz->p->p->color=RED;
				pz=pz->p->p;
			}else
			{
				if(pz==pz->p->left)
				{
					pz=pz->p;
					right_rotate(pz);
				}
				pz->p->color=BLACK;
				pz->p->p->color=RED;
				letf_rotate(pz->p->p);
			}
		}
	  }
	root->color=BLACK;//这里的做法,可以避免插入的节点成为根节点,黑高度还是不受影响,只是大了一个
//	delete py;
}
/**
插入算法理解:插入的pz为红,这样可以避免节点黑高度的影响,以pz的父节点为黑与否作为循环的标准判定,这时候只存在一种意外情况即:根节点是pz,pz的父为黑的,那么的话
就会影响rbtree的属性,但是最终结尾的时候我们有一个修改根的颜色可以保证,此外当pz的父节点是红的时候,我们可以根据上面的情况判断存在祖父节点,且为黑,此外的话
pz,pz的父节点为红,这是基本的属性情况,下面分三种也就是算法导论所述的情况,即叔叔节点为红,叔叔节点为黑时候pz为右子树和左子树的三种情况
情况1:为了使红黑树的五个性质满足,从祖父点开始,父辈那层为红,为了避免pz,pz父辈的同色,修改颜色即可,这样的话祖父为红,是唯一的纰漏,相应的向上回溯即可:
情况2:对于这种父辈不同色的情况是存在的,比如叔叔的是一个nil,父为红,接着接nil,为了满足红黑树的五个属性根据上面分析父,祖父颜色还是那样的,为了下一步使黑高度
满足,且pz,pz父颜色不同,进行了所谓的旋转。

时间复杂度:由于一开始的insert()是对一颗二叉树插入一个点,那么一般情况下的话就是树的高度o(lgn),后面的rb_insert_fixup()主要是一个while也是while树的高度
所以时间复杂度是o(lgn)

**/
void rbtree::rb_insert(node* pz)//红黑树是二叉查找树,插入节点先图红,可以保证节点黑高度,插入的是类似二叉查找树的插入
{
	node* py=nil;
	node* px=root;
	while(px!=nil)
	{
		py=px;
		if(pz->key>px->key)
			px=px->right;
		else
			px=px->left;
	}
	pz->p=py;
	if(py==nil)
		root=pz;
	else 
		if(pz->key<py->key)
			py->left=pz;
		else
			py->right=pz;
	pz->left=nil;
	pz->right=nil;
	pz->color=RED;
	rb_insert_fixup(pz);
//	delete py;
//	delete px;
}
/****************习题******************************/
//13.3-1  ans:会影响节点黑高度
/*13.3-2 ans:
			    38(black)
          19(red)    41(black)
    12(black) 31(black)
8(red)	
*/

/******************rbtree_delete()*******************************/
node* rbtree::rb_min(node* pz)
{
	while (pz->left!=nil)
	{
		pz=pz->left;
	}
	return pz;
}

node* rbtree::rb_successor(node* pz)//中序遍历求后继
{
	node* py=new node();
	if(pz->right!=nil)
		return rb_min(pz->right);
	py=pz->p;
	while (py!=nil&&pz==py->right)
	{
		pz=py;
		py=py->p;
	}
	return py;
}

void rbtree::rb_delete_fixup(node* pz)
{/*
这个函数用来修改删除一个黑节点之后的子树,因为如果是删除红色,木有影响但是删除的是黑色的话就会有影响比如说黑高度影响,以及删除之后
会造成父子同为红色的可能性以及根节点为红色等可能性,所以需要进行调整,有一个while循环,循环会造成向上回溯,回溯到最终的终结的话就是
要么到达根节点,要么是节点颜色属性是红的(这样为了满足黑高度可以把这个点染黑),根据二叉树的删除来计算的话,删除的那个点的下一个点必
为单个的情况,要么左子树,要么右子树,要么无子树,相应的作为判断的pz点,就可以用其颜色作为while标准,为红会后续的直接涂黑(直接增加黑高度)
为黑的话,就要进行while的操作。	
*/
	node* pw=new node();
	while(pz!=root&&pz->color==BLACK)//为根节点,整体黑高度不影响,为红,后面会涂黑。
	{
		if(pz==pz->p->left)
		{
			pw=pz->p->right;
			if(pw->color==RED)//只有是父节点是黑色的情况pw才可能是红色。
			{
				pz->p->color=RED;
				pw->color=BLACK;
				letf_rotate(pz->p);
				pw=pz->p->right;
			}//情况1
			if (pw->left->color==BLACK&&pw->right->color==BLACK)
			{
				pw->color=RED;//只用堆pw修改,pz实际上是黑的,去掉一重还是黑,不要改,但是pw要;
				pz=pz->p;//此操作结束后,pz为红,结束并涂黑,如果pz为黑,还要pz加一重黑
			}//情况2:去掉一重黑,让存在双重黑的pz变正常,如果是从情况1来,就是pz为红的了,如果不是的话,pz为黑,按照算法导论所述,pz让存在
			//双重黑,主要是保证该树黑高度,从pz节点起的黑高度和没有删某个黑节点一样的,主要是pz的父那个节点开始少一个黑高度。
			else
			{
				if(pw->right->color==BLACK)//情况3
				{
					pw->left->color=BLACK;
					pw->color=RED;
					right_rotate(pw);
					pw=pz->p->right;
				}//情况4
				pw->color=pz->p->color;
				pz->p->color=BLACK;
				pw->right->color=BLACK;
				letf_rotate(pz->p);
				pz=root;
			}
		}
		else if(pz==pz->p->right)
		{
			pw=pz->p->left;//叔叔
			if(pw->color=RED)
			{
				pz->p->color=RED;
				pw->color=BLACK;
				right_rotate(pz->p);
				pw=pz->p->left;
			}
			if(pw->left->color==BLACK&&pw->right->color==BLACK)
			{
				pw->color=RED;
				pz=pz->p;
			}
			else 
			{
				if(pw->right->color==BLACK)
				{
					pw->left->color=BLACK;
					pw->color=RED;
					letf_rotate(pw);
					pw=pz->p->left;
				}
				pw->color=pz->p->color;
				pz->p->color=BLACK;
				pw->right->color=BLACK;
				right_rotate(pz->p);
				pz=root;//只要到达情况4就可以完成了,因为已经添加一个黑高度,所以用此结束循环。
			}
		}	
	}
	pz->color=BLACK;
//	delete pw;
	//上面的四种情况不一定是按照顺序来的,但是最终的解决都是通过情况4进行解决,前面三种情况通过转换最后都会变为情况4,那么相应的
	//对于情况4,而言我们本身是需要pz(删除的节点的下一个点),含有2重黑色,但是实际上是要求pz上面一层添加一个黑色,情况4就是如此做
	//通过把pw的颜色换pz的父颜色,父色变黑,在左旋,那么造成,pz的上面一层为父(黑),上面一层为pw原来颜色(父变黑前色),pw的右子树由
		//红变黑,那么这边对应黑高度不变。无论是那种情况最终都是变为情况四处理的。
}
/*


时间复杂度:o(lg(n))
*/
node* rbtree::rb_delete(node* pz)
{
	node* py=new node();
	node* px=new node();
	if (pz->left==nil||pz->right==nil)//pz这节点有单个左或者单个右或者全无,反就是有左有右
		py=pz;
	else 
		py=rb_successor(pz);//如果是有左右子树,就是求后继,下面的操作无论是对于后继还是非后继都是一样的。
	if(py->left!=nil)
		px=py->left;
	else 
		px=py->right;
	px->p=py->p;
	if(py==nil)
		root=px;
	else 
		if(py==py->p->left)
			py->p->left=px;
		else
			py->p->right=px;
	if(py!=pz)
		pz->key=py->key;
	if (py->color==BLACK)//若是一个黑的话,会影响黑高度,所以要调整
	{
		rb_delete_fixup(px);
	}
//	delete py;
//	delete px;
	return py;
}

//

/*********************main_function()************************************/

int main()
{
	rbtree* rb=new rbtree();
	node* p1=new node(1);
	node* p2=new node(2);
	node* p3=new node(3);
//	rb->letf_rotate(p1);//一开始运行此处的时候会报错,原因很简单,在上面的py->left等都是空的。所以赋值的时候会出错的。勿怪了
//	rb->right_rotate(p2);
	rb->rb_insert(p1);
	rb->rb_insert(p2);
	rb->rb_insert(p3);
	rb->rb_delete(p2);
	delete p1;
	delete p2;
	delete p3;
	return 0;
}

/*
stl容器使用rbtree为底层实现的细节解释:
*/




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值