红黑树-》代码汇总

  红黑树是二叉搜索树的一种,也是符合一定平衡性的平衡树,它的平衡性比AVL稍微差点,但是它在从一个不平衡状态恢复到平衡状态要比AVL要快。这种快体现在删除节点时恢复到平衡状态时。

        红黑树必须满足一下性质:

1、每个节点要么是红色要么是黑色。

2、根节点只能是黑色。

3、外部节点都是黑色。(外部节点,不是二叉树的一部分,没有实际意义,在程序中所有为空的子树都指向同一个黑色标记节点,来处理边界判断。)

4、一个为红色的节点,它的两个儿子必须是黑色。

5、每个节点,从该节点到到其子孙节点的所有路径上包含的黑节点的数目相同。

看数据结构对红黑树各种不平衡然后怎么平衡的分析,看的让我乱的很,觉得还是要步步为营,代码和理论结合,比较好掌握。

解决问题的步骤

1、左旋右旋搞定先,在平衡中肯定用的到。

2、插入节点,在插入节点后,就要维护平衡了,这是让人百转千挠的开始。

3、插入节点后根据引起不平衡的原因来恢复其平衡。插入不平衡的情况,根据左右分对称两大种,左右之中又分三种情况。算法导论上对着三种情况处理的可以说很唯美了。

在处理不平衡情况,当前不平衡状态在处理后转化为后一种情况。这三种情况可以处理任何的不平衡情况。最终使其平衡。

4、然后代码和图去结合,根据代码自己去画图,不管别人讲的再怎么浅显易懂,还是要自己去代码和图相结合那么反复的去屡屡,屡顺了,就成了你自己的东西了。一般都可以屡清楚的。

5、删除节点,在删除后,就要维护平衡,这更是让人纠结的发源地。

6、删除的不平衡,算法导论在处理上有个问题转换的步骤,这点要想通透了。他是将当前平衡节点加上了双重色,也就是当前节点的父亲“被杀了”,说的有点残忍,但他的钱财留给他的孩子,这里也就是他的颜色。这种转换其实是“无中生有”的一种假设。那么现在树的情况是这样的,当前节点具有两种颜色。也就是违反了上面提到的性质1。而红黑树的其他条件都满足。

       如果父亲留下的是红色,那么整个树还是平衡的。不用再纠结了。而如果父亲留下的是黑色,当前节点是红色,那么直接把黑色继承过来。整个树还是“和谐的”。而如果父亲留下的是黑色的而当前节点也是黑色的,那就打破了树的“和谐”规则,要把这个黑色向上移动,直到黑色交给一个红色或是根节点了,或是做合适的旋转和颜色更改,来释放具有双黑色的节点。

#pragma once
enum Color{Red,Black};
struct cNode
{
	Color color;
	int  mvalue;
	cNode *parent,
		*left,
		*right;
};
class RBTree
{
public:
	RBTree(void);
	~RBTree(void);
	//插入新的节点
	RBTree& RBInsert(int key)
	{
		Insert(key,pFlag);
		return *this;
	}
	//删除节点
	RBTree& RBDelete(int key)
	{
		Delete(key,pFlag);
		return *this;
	}
private:
	//插入节点的实际操作
	void Insert(int key,cNode* t=NULL);
	//删除节点的实际操作
	void Delete(int key,cNode* t=NULL);
	//返回真正删除的节点指针
	cNode*  TreeSuccessor(cNode* root,int key,cNode* t=NULL);
	//左旋
	void LeftRotate(cNode* c,cNode* t=NULL);
	//右旋
	void RightRotate(cNode* c,cNode* t=NULL);
	//插入平衡维护
	void InsertFixUp(cNode* c,cNode* t=NULL);
	//删除平衡维护
	void DeleteFixUp(cNode* c,cNode* t=NULL);
private:
	cNode* pRoot;//根节点
	cNode* pFlag;//额外标记节点,处理边界问题
};
 要平衡左旋和右旋是少不了的,左旋右旋实现相对简单

左旋代码:

void RBTree::LeftRotate(cNode* c,cNode* t/* =NULL */)
{
	cNode* y=c->right;
	c->right=y->left;
	if(y->left!=t)
		y->left->parent=c;
	y->parent=c->parent;
	if(c->parent==t)
		pRoot=y;
	else if(c==c->parent->left)
	{
		c->parent->left=y;
	}else
		c->parent->right=y;
	y->left=c;
	c->parent=y;
}
右旋代码:
void RBTree::RightRotate(cNode* c,cNode* t/* =NULL */)
{
	cNode* y=c->left;
	c->left=y->right;
	if(y->right!=t)
	{
		y->right->parent=c;
	}
	y->parent=c->parent;
	if(c->parent==t)
		pRoot=y;
	else if(c==c->parent->left)
		c->parent->left=y;
	else
		c->parent->right=y;
	y->right=c;
	c->parent=y;
}

插入新节点代码:

       插入的新节点其颜色设置为红色。因为如果设置为黑色则必定破坏了平衡性。而设置为红色则是可能破坏了。破坏的情况为,新节点的父节点颜色也为红色。这时就需要做平衡性的维护了。

void RBTree::Insert(int key,cNode* t/* =NULL */)
{
	cNode* y=t;
	cNode* x=pRoot;
	while(x!=t)
	{
		y=x;
		if(x->mvalue>key)
			x=x->left;
		else if(x->mvalue==key)
			return ;
		else
			x=x->right;
	}
	cNode* z=new cNode();
	z->parent=y;
	if(y==t)
	{
		pRoot=z;
	}
	else if(key<y->mvalue)
		y->left=z;
	else
		y->right=z;
	z->left=t;
	z->right=t;
	z->color=Red;
	//维持平衡
	InsertFixUp(z,t);
}

插入新节点维护树平衡性的代码


void RBTree::InsertFixUp(cNode* c,cNode* t/* =NULL */)
{
    while(c->parent->color==Red)
    {
        cNode* gc=c->parent->parent;
        cNode* pc=c->parent;
        cNode* y=t;
        if(pc==gc->left)
        {
            y=gc->right;
            if(y->color==Red)
            {
                pc->color=Black;//情况1
                y->color=Black; //情况1
                gc->color=Red;//情况1
                c=gc;                //情况1
            }
            else
            {
                if(c==pc->right)
                {
                    c=pc;             //情况2
                    LeftRotate(c,t);//情况2
                }
                c->parent->color=Black;//情况3
                c->parent->parent->color=Red;//情况3
                RightRotate(c->parent->parent,t);//情况3
            }
            
        }
        else
        {
            y=gc->left;
            if(y->color==Red)
            {
                pc->color=Black;
                y->color=Black;
                gc->color=Red;
                c=gc;
            }
            else
            {
                if(c==pc->left)
                {
                    c=pc;
                    RightRotate(c,t);
                }
                c->parent->color=Black;
                c->parent->parent->color=Red;
                LeftRotate(c->parent->parent,t);
            }
        
        }
    }
    pRoot->color=Black;
}



情况1:如图1所示,经过情况1的处理后如图2

                              

                     图1                                                                                   图2

        这时候程序循环重头进行,现在节点7是C,节点14是y如图3。再继续处理变成如图4的情况,这时变成情况3,再接着处理。变成图5.平衡了现在。

                                                                         

                             图3                                                              图4

                                                    

                                 图5

删除节点代码:

  在删除节点时使用了一个TreeSuccessor的函数来获取删除节点。

void RBTree::Delete(int key,cNode* t/* =NULL */)
{
    cNode* y=TreeSuccessor(pRoot,key,t);
    cNode* x=t;
    if(y->left!=t)
    {
        x=y->left;
    }
    else
        x=y->right;
    x->parent=y->parent;
    if(y->parent==t)
        pRoot=x;
    else if(y==y->parent->left)
        y->parent->left=x;
    else
        y->parent->right=x;
    if(y->color==Black)
        DeleteFixUp(x,t);
    delete y;
    return ;

}


这个函数的作用就是是,要删除的节点变成为要么只有左子树要么只有右子树,要么没有子树。

cNode* RBTree::TreeSuccessor(cNode* root,int key,cNode* t)
{
    cNode* p=root;
    cNode* pp=0;
    while(p!=t)
    {
        pp=p;
        if(p->mvalue>key)
            p=p->left;
        else if(p->mvalue==key)
            break;
        else
            p=p->right;
    }
    if(p->right!=t&&p->left!=t)
    {
         p->mvalue=p->left->mvalue;
         key=p->mvalue;
         TreeSuccessor(p->left,key,t);
    }
    return p;
}

     

 删除一个节点后,平衡性维护的代码:

 

void RBTree::DeleteFixUp(cNode* c,cNode* t/* =NULL */)
{
    while(c!=pRoot&&c->color==Black)
    {
        if(c==c->parent->left)
        {
            cNode* pc=c->parent;
            cNode* w=pc->right;
            if(w->color==Red)
            {
                w->color=Black;       //情况1
                pc->color=Red;         //情况1
                LeftRotate(pc,t);        //情况1
                w=pc->right;             //情况1
            }
            if(w->right->color==Black&&w->left->color==Black)
            {
                w->color=Red;              //情况2
                c=c->parent;               //情况2
            }
            else if(w->right->color==Black)
            {
                w->color=Red;              //情况3
                w->left->color=Black;      //情况3
                RightRotate(w,t);          //情况3
                w=c->parent->right;         //情况3
                w->color=c->parent->color;   //情况4
                c->parent->color=Black;      //情况4
                w->right->color=Black;       //情况4
                LeftRotate(c->parent,t);      //情况4
                c=pRoot;                      //情况4
            }
        }
        else
        {
            cNode* pc=c->parent;
            cNode* w=pc->left;
            if(w->color==Red)
            {
                w->color=Black;
                pc->color=Red;
                RightRotate(pc,t);
                w=pc->left;
            }
            if(w->right->color==Black&&w->left->color==Black)
            {
                w->color=Red;
                c=c->parent;
            }
            else if(w->left->color==Black)
            {
                w->color=Red;
                w->right->color=Black;
                LeftRotate(w,t);
                w=c->parent->left;
                w->color=c->parent->color;
                c->parent->color=Black;
                RightRotate(c->parent,t);
                c=pRoot;
            }
        }
    }
    c->color=Black;
}


       在解决删除问题上使用了“无中生有”双重色方法,但它违反了节点只能是单色的原则。要解决这个问题就要把这个黑色向上移动,直到黑色交给一个红色或是根节点了,或是做合适的旋转和颜色更改,来释放具有双黑色的节点。

        代码中处理情况1,使其成为情况2,或是情况3、4。进行处理。而情况2的处理就是把双重色向上移,情况3,4的处理就是旋转和变色来消解双重色。

情况1的处理:

                                                

                               情况1变换前                                                           情况1变换后


       这时候如果w两个左右子树的根节点都是黑色,则进行情况2处理变换后c为灰色表示c为双色节点。这是程序循环又从头开始,处理

                                                                       


                  情况2变换前                                                     情况2变换后


 如果经过情况1处理后新的W节点只有其右子树的根为黑色,则进行情况3的变化。


                                                 

      情况3变换前                                                     情况3变换后

        经过情况3的处理后,就可以直接情况4的处理,来稀释双重色节点了。这里的灰色表示是红色或者是黑色都可以没关系了。再经过情况4处理后相当于双重色节点c上又补了一个黑色的节点。代替了被删除的黑色节点,这时候树整个都平衡了。这里会有疑问那原来的6节点是什么颜色,怎么办。变化后由原来的节点7补上,而把6节点原来的颜色(不管他是什么颜色赋给7节点,7再旋转代替6的位置)赋给7节点。这时候c就不用再装成双重色节点了。而是真的多出了一个黑节点。“无中生有”完成。


                                     

                  情况4变换前                                                              情况4变换后


           代码可能有bug。发现了还望指正,谢谢。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值