RBtree红黑树理解和代码

1,红黑树简介

红黑树是一种自平衡的树,虽然平衡二叉树也是一种平衡的二叉树,但他的平衡操作比较复杂,因而要有红黑树,通过红黑树的一些规则使我们不需要知道整个树的结构就可以在有限的操作内使树平衡。
红黑树的规则有四条:

  1. 节点必为红或黑
  2. 根节点必为黑
  3. 红色节点和红色节点不能连接
  4. 从任意节点出发到它的终端节点经过相同的黑色节点数量

以上四条规则较为复杂的是第三条和第四条,正是这两条规则使红黑树发挥作用。

2,红黑树的插入

红黑树插入的新节点N人为规定为红色节点,之所以这么做是因为方便,设想如果插入的是黑色节点,那么一定触发第4条矛盾,即增加了一个方向上黑色节点的层级,我们后面称作秩。
每当有一个新的节点要插入时,我们总是先找到他应当挂载在哪个节点下面。

2.1挂载在黑色节点下面

如果挂载在黑色节点下面由于我们插入的是红色节点所以不会触发第4条矛盾,同样第3条矛盾也不会触发。所以直接插入即可。

2.2挂载在红色节点下面

由于触发了第3条矛盾所以我们需要进行处理。
由于父亲节点P为红色所以父亲节点的父亲节点PP必为黑色(树在插入前总是正确的),而PP节点的另一个儿子,即P节点的兄弟节点U我们分为两种情形。1,U为黑色或NULL;2,U为红色。

2.2.1 U为黑色或无

当U为黑色或无时,我们进行旋转处理,为什么进行旋转处理呢?
推理如下:
由于P为红色和N矛盾,所以我们将P变成黑色化解矛盾3,但是P变为黑色增加了秩(黑色层级),所以必须要将这个秩消解,假设我们的P是在左边,那么我们将P放到PP的位置上去,这样左边就少了一个秩,达到了目的,同时被替代的PP下放到右边,注意PP原来是黑色的,为了使右边不增加秩,我们得把PP变为红色。这样也就处理完毕。

注意,当N和P同时为各自父亲的右节点或左节点时才能直接用P代替PP,否则我们得先用N代替P然后P作为N的子节点,也就是拉直成同向节点,转化为上面描述情形。

2.2.2 U为红色节点

当U为红色节点时,我们有以下推理:
由于P为红色和N矛盾,所以我们将P变成黑色化解矛盾,但同时触发了矛盾4,所以我们将PP变为红色这样N这边的秩就仍旧平衡,但是U这边的秩就少一,但是由于U是红色,所以把U变为黑色则这边的秩也就平衡。
到这里还未处理完毕,由于我们将PP设置为了红色,有可能PP和他的父亲触发了矛盾3,解决办法就是递归处理,将PP看做需要平衡的新节点继续进行从标题 2开始的判断操作。这样不断递归要么递归到挂载的父亲节点是黑色节点结束,要么一直递归到根节点,而根节点规定必为黑色。

3,红黑树的删除

红黑树的删除实际上从某种意义上来说和插入有同构的地方,如果说插入过程主要是看叔叔节点为红色或是黑色(NULL也类似黑色的处理方式),那么删除就主要看侄子节点的颜色。
为了把红黑树的操作更容易记忆,也更容易理解,我们用一些生活中的知识来类比。
红黑树即是一个家族,家族中红色代表候补的劳动力,黑色代表工作劳动力。
一个家庭中不能出现相连的红色的节点,我们理解为一个家庭要是两代人都不劳动,那这个家就得饿死。
先建立这样的直观感受,我们再来进行后面的描述。

3.1 删除的节点只有一个儿子

这种节点一定为黑色,且他那个儿子是红色。很简单,红色节点要是有一边黑色一边为空,那么一定秩不平衡。也直观理解为你如果正当壮年却想颐养天年那你必须得生出两个儿子帮你做事。
在这种情况下我们用儿子节点替代父亲节点即可,编程的话可以直接把父亲的值修改成儿子的值然后把儿子节点删除。

3.2 删除的节点有两个儿子

这种情况下我们用该节点的后继节点来代替它,何谓后继节点?
即大于他的最小节点,编程的话即先往他的右子树走,然后找右子树家族中的最左节点。既然是最左节点那么该节点一定没有左子树,如果有右子树则转换为3.1,如果没有右子树则转换为3.3

3.3 删除的节点没有儿子

当我们删除节点D时同样需要考虑矛盾3和4。

3.3.1删除的是红色节点

由于删除红色节点不影响矛盾3和4,所以直接删除。

3.3.2删除的是黑色节点

由于删除黑色节点触发了矛盾4,秩不平衡了。所以要进行处理.
分为以下情形

注意在下面的操作中注意类比插入操作,有一定的镜像性。

3.3.2.1 删除节点为黑,兄弟为红

当兄弟B为红时,父亲节点P一定是黑色的,且侄子节点也一定是黑色的。
意味着兄弟随时可以振作起来作为工作劳动力。那么父亲P就可以去支援D家族,那么D家族虽然要少一个秩,但父亲又把这个秩顶替上了,所以平衡了。同时父亲的位置由兄弟节点B变黑然后顶替,由于兄弟节点本来在他那个家族就没有贡献秩,所以他离开不会减少他原来家族的秩。

3.3.2.2 删除节点为黑,兄弟为黑

此时我们又分为以下三种情形
1,侄子节点有一个或者两个红色。
这种情况下意味着兄弟家族还是可以补充劳动力的,所以父节点变为黑色去支援D节点家族,B节点则变为变为父节点原来的颜色顶替他的位置。似乎和上面3.3.2.1的情形一样,但注意此时兄弟家族的秩减少了1,因为B节点原来是黑色的现在已经去顶替父亲的工作了。但庆幸B家族有一个红色节点可以顶替节点B。
2,侄子节点没有一个红色(全为黑色或者全为NULL)
这种情况下显然兄弟节点显然不能顶替父亲节点,因为去了之后自己家族无法顶上他的工作。
所以这种情况下又分为两种子情形:
2.1,父亲节点为红色
由于父亲节点为红色于是他可以变为黑色,这样两边的家族都将增加一个秩,D家族减少的秩被加上而平衡,而B家族平白无故加了一秩所以B变为红色选择养老,自减一秩。
2.2,父节点为黑色
这时确实各个方面都没有候补劳动力了,于是为了平衡兄弟节点B变为红色,也就是B家族秩减一,D家族秩也减一,然后把父节点视作新的节点D,回到标题3继续递归判断(仅靠父亲这一家族已经无法处理了,往上申报,交给祖宗处理)

代码实现

#include <iostream>
enum Color{RED,BLACK};
const int RIGHT = 0;
const int LEFT = 1;
class Direction
{	
public:
	int mD;
	Direction(int d) :mD(d) {};
	int opposite() const
	{
		return mD == RIGHT ? LEFT : RIGHT;
	}
};
class Node
{
public:
	int value;
	Color c;
	Node * father;
	Direction mD;
	Node *lch, *rch;
	Node(int _value, Color _c, Node * _father, int _md, Node* _lch = nullptr, Node* _rch = nullptr) :
		value(_value), c(_c), father(_father), mD(_md), lch(_lch), rch(_rch) {};
	Node()=default;
	Node* getChild(int Direction)
	{
		if (Direction == RIGHT)
			return this->rch;
		else return this->lch;
	}
	void deleChild(int Direction)
	{
		if (Direction == RIGHT)
			this->rch = nullptr;
		else this->lch = nullptr;
	}
	void setChild(int Direction, Node* child)
	{
		if (Direction == RIGHT) this->rch = child;
		else this->lch = child;
	}
	static void rotation(Node* T, Node* p, Node* pp)
	{	//暂存T的direction
		Direction d = T->mD;
		Node* tch = T->getChild(d.opposite());
		T->mD = p->mD;
		T->father = pp;
		if(pp!=nullptr)
			pp->setChild(T->mD.mD, T);
		p->mD =Direction(d.opposite());
		p->father = T;
		T->setChild(p->mD.mD, p);
		if (tch != nullptr)
		{
			tch->mD = Direction(d);
			tch->father = p;
			p->setChild(tch->mD.mD, tch);
		}
		else
		{
			p->setChild(d.mD, nullptr);
		}
	}
};
static Node* root;
//hasexisted==0是没有找到,返回的是父亲,fflag==1是找到了,返回自己。
static int hasExisted = 0;
Node *findNode(int value, Node* R = root)
{
	if (R == nullptr) return nullptr;
	Node * p = R;
	if (p->value == value) { hasExisted = 1; return p; }
	else if (p->value > value&&p->lch != nullptr)
		return findNode(value, p->lch);
	else if (p->value < value&&p->rch != nullptr)
		return findNode(value, p->rch);
	hasExisted = 0;
	return p;
}
int dealInsert(Node* T)
{
	int flag;
	//flag==1,uncle is RED,flag==2,uncle is black or not exsited.
	if (T == root) {
		T->c = BLACK;
		return 1;
	}
	Node* p = T->father;
	if (p->c == BLACK) return 0;//p为红色则必有pp
	Node* pp = p->father;
	Node* U = pp->getChild(p->mD.opposite());
	if (U != nullptr&&U->c == RED) flag = 1;
	else if (U == nullptr || (U->c == BLACK)) flag = 2;
	switch (flag)
	{
	case 1:
		p->c = BLACK;
		U->c = BLACK;
		pp->c = RED;
		dealInsert(pp);
		break;
	case 2:
		//拉直
		if (T->mD.mD != p->mD.mD) Node::rotation(T, p, pp);
		Node::rotation(T, pp, pp->father);
		T->c = BLACK;
		pp->c = RED;
		break;
	}
	return 0;
}
int insertNode(int N)
{
	if (root == nullptr) return -1;
	Node* p=findNode(N);
	if (!hasExisted)
	{
		if (N > p->value)
		{
			Node* T = new Node(N,RED,p,RIGHT);
			p->rch = T;
			dealInsert(T);
		}
		else {
			Node* T = new Node(N, RED, p, LEFT);
			p->lch = T;
			dealInsert(T);
		}
	}
	return 0;
}
int dealDelete(Node * T)
{
	if (T = root) return 1;
	//删除操作不能在deal中进行,红色直接跳过
	if (T->c != RED)
	{
		Node* p = T->father;
		Node* b = T->father->getChild(T->mD.opposite());
		if (b->c == RED)
		{
			b->getChild(b->mD.opposite())->c = RED;
			b->c = BLACK;
			Node::rotation(b, p, p->father);
		}
		else//于是必有兄弟
		{
			Direction D(b->mD);
			//兄弟的儿子至少有一个红色
			if (b->getChild(D.mD) != nullptr&&b->getChild(D.mD)->c == RED)
			{
				b->c = p->c;
				p->c = BLACK;
				b->getChild(D.mD)->c = BLACK;
				Node::rotation(b, p, p->father);
			}
			else
			if (b->getChild(D.opposite()) != nullptr&&b->getChild(D.opposite())->c == RED)
			{
				Node* nb = b->getChild(D.opposite());
				nb->c = BLACK;
				b->c = RED;
				Node::rotation(nb, b, p);
				nb->c = p->c;
				p->c = BLACK;
				b->c = BLACK;
				Node::rotation(nb, p, p->father);
			}
			else
			{//侄子两个黑色或两个空
				if (p->c == RED)
				{
					p->c = BLACK;
					b->c = RED;
				}
				else
				{
					b->c = RED;
					dealDelete(p);
				}
			}
		}
	}
	return 0;
}
int deleteNode(int N)
{
	if (root == nullptr) return -1;
	Node* T = findNode(N);
	if (!hasExisted) return -1;
	Node* K;
	S: //两个都不存在
	if (T->rch == nullptr&&T->lch == nullptr)
	{
		if (T == root) return -1;
		dealDelete(T);
		T->father->deleChild(T->mD.mD);
		return 0;
	}	//两个都存在
	else if (T->rch != nullptr&&T->lch != nullptr)
	{	//找后继
		K = T->rch;
		while (K->lch != nullptr)
			K = K->lch;
		T->value = K->value;
		T = K;
		goto S;
	}	//只存在一个
	else
	{
		if (T->rch != nullptr)
		{
			T->value = T->rch->value;
			T = T->rch;
			goto S;
		}
		else
		{
			T->value = T->lch->value;
			T = T->lch;
			goto S;
		}
	}
	return 0;
}
int main()
{
	return 1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值