数据结构——红黑树代码

#define  _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:6031)
#include<stdio.h>
#include<stdlib.h>
#include<bits/stdc++.h>
using namespace std;
/*
	红黑树的操作:
	1.查找:同二叉排序树(后文称BST)的查找操作

	2.插入:(1)找插入的位置:同BST的插入操作,先把节点插入到树中
		    (2)确定节点的初始颜色:
				Ⅰ. 先假设为黑色,则一定破坏“黑路同”这一性质,
					所以每次插入元素都需要调整,这与我们设计红黑树的理念相悖:放宽了对AVL树对于平衡因子的约束
					因此不能为黑色。
				Ⅱ. 所以节点只能是红色,尽管当插入的节点的父亲也是红色节点时会破坏“不红红”这一性质,但是并不是每次插入都需要调整
			注:如果是在一颗空树上插入根节点,则根节点直接作为黑色
			(3)如何调整?
				Ⅰ. 染色:红变黑,黑变红
				Ⅱ. 旋转:同AVL树的旋转,LL,LR,RL,RR
			(4)分类讨论:
				Ⅰ. 树为空,插入根节点,根节点赋为黑色
				Ⅱ. 树不为空,插入节点x为红色,
					如果x的父亲(p)为黑色,则无需调整;
					如果x的父亲为红色(根据性质可以推导出爷爷一定是黑色),则需要调整:
						再根据x的叔叔节点(u)颜色分类:
						(1)叔叔节点为红色:将父亲(变黑),叔叔(变黑),爷爷(变红)节点均变色,然后将爷爷节点作为x不断处理,直到根节点(如果x为根节点,将其设置为黑色)
						(2)叔叔节点为黑色:
							Ⅰ. LL:将父节点设为黑色,爷爷节点设为红色,以爷爷节点为支点右旋
							Ⅱ. LR:以父节点为支点左旋,转换成LL情况(左旋后父子关系发生改变,因此需要交换两个指针(x和p))
							Ⅲ. RR:将父节点设为黑色,爷爷节点设为红色,以爷爷节点为支点左旋
							Ⅳ. RL:以父节点为支点右旋,转换成RR情况(右旋后父子关系发生改变,因此需要交换两个指针(x和p))
		口诀:是根为黑,非根为红
			破坏“不红红”,要看叔节点
			红叔长辈染,爷爷变新点
			黑叔要旋转,代码要统一,两遍变一遍,还是父爷染

	3.删除:(1)按照BST的删除操作(删除x):
				Ⅰ. x的度为2,用中序遍历x的前驱或者后继节点y的数据替换x,问题转化为删除度为y节点
					以y为x的中序遍历前驱节点为例,y为x左子树中最靠右的节点,因此y一定没有右孩子,换句话说y的度一定是0或1
					因此可以转化为Ⅱ的情况
				Ⅱ. x的度为1或者0,直接用x唯一的孩子y顶替x的位置(度为零的孩子为空节点)
			(2)调整:关注x和y的颜色(假设y是父亲,x是y唯一的一个孩子)
				按照x和y的颜色进行分类讨论:
				Ⅰ. y红x黑:此时不破坏任何性质,不需要调整
				Ⅱ. y黑x红:可能破坏“不红红”(需要看y的父亲),一定破坏“黑路同”(黑色节点少了一个)
							此时直接将x变色(赋值为黑色)即可
				补充概念:
					双黑节点:删除一个度为0或1的黑色节点之后,其黑色的孩子顶替了y的位置,顶替之后我们将x对黑高的贡献为2
							 此时Ⅲ这种情况我们只需要思考如何将双黑节点变成普通黑色节点即可。
					注:双黑节点只是我们为了方便理解,实际红黑树中并不存在这种节点

				Ⅲ. y黑x黑:调用Ⅱ的代码后x替换了y的位置,接下来根据x是p的左孩子还是右孩子进行分类讨论。
						   假设p为x的父节点,w为x的兄弟节点(即x顶替y之后,p有两个孩子分别是x和w)
						  (1)x是右,w是左(L):再根据w与w的孩子的颜色进行分类。
							 (a)w是黑色,w的孩子都为黑色:x褪一层黑色(逻辑上的操作,实际代码没有体现)w变色(变红),
														  x和w褪掉的黑色交给p,如果p原本是红色	,则p变色(变黑),结束;
														  否则p变成双黑节点,作为新的x,直到x的父亲y为红色或者不存在(根节点)
														  (注:到根节点才停止会导致整棵树的黑高-1)					
							 (b)w是黑色,w的孩子至少有一个红色(下称r)的:
								(注:w如果两个孩子都是红色,则作为b1和b2哪种情况都行,这里默认将左孩子看成红色,视为b1的情况)
								(b1)w的左孩子是红色(LL):先以p为中心右旋,然后将r变色(变黑),【再将w变成p的颜色,最后将p赋成黑色】
															(变色:r(w->r)变w,w变p,p变黑,然后以p为中心右旋)
														注:正常需要根据p的颜色进行分类讨论,【】部分为p是红色时多进行的操作,
															但是我们发现当p为黑色的时候加上这两步也不会出错,因此我们直接统一这两种情况,只写一套代码
								(b2)w的右孩子是红色(LR):先将w和r变色,再以w为中心左旋,旋转之后将w作为p的左孩子(w = p->l),转化成LL的情况
							 (c)w是红色,w的孩子一定为黑色:w变色(变黑),p变色(变红),以p为中心右旋,旋转之后将w作为p的左孩子(w = p->l)
															转化成了(a)或者(b)
						  (2)x是左,w是右(R):和(1)是对称的,左变右,右变左
*/
#define RED 0 //红色
#define BLACK 1 //黑色
typedef struct RBTreeNode
{
	int data;//数据
	struct RBTreeNode* l;//左孩子
	struct RBTreeNode* r;//右孩子
	int color;//颜色
	struct RBTreeNode* parent;//父亲节点
}RBTNode, *RBTree;

//创建一个新节点
RBTNode* creatNode(int key, RBTNode* parent, RBTNode* l, RBTNode* r)
{
	RBTNode* s = (RBTNode*)malloc(sizeof(RBTNode));
	if (s != NULL)
	{
		s->data = key;
		s->parent = parent;
		s->l = l;
		s->r = r;
		s->color = RED;
	}
	return s;
}

//以红黑树的x节点为中心进行左旋(这里不仅需要维护左右孩子指针,还需要维护父亲指针)
RBTree left_rotate(RBTree root, RBTNode* x)
{
	//y指向x的右孩子
	RBTNode* y = x->r;
	//y的左孩子变成x的右孩子(处理y和x的孩子的关系)
	if (y != NULL)
	{
		x->r = y->l;
		if (y->l != NULL)
		{
			y->l->parent = x;
		}
		//y的父亲变成x的父亲(处理y和x的父亲的关系)
		y->parent = x->parent;
		if (x->parent == NULL)//如果x是根节点,旋转后需要更新root的指向
		{
			root = y;
		}
		else//如果x不是根节点,则需要判断x是x->parent的左孩子还是右孩子
		{
			if (x == x->parent->l)
			{
				x->parent->l = y;
			}
			else
			{
				x->parent->r = y;
			}
		}
		//y的左孩子是x,x的父亲是y(处理y和x的关系)
		y->l = x;
		x->parent = y;
	}
	return root;
}
//以红黑树的x节点为中心进行右旋(这里不仅需要维护左右孩子指针,还需要维护父亲指针)
RBTree right_rotate(RBTree root, RBTNode* x)
{
	//y指向x的左孩子
	RBTNode* y = x->l;
	//y的右孩子变成x的左孩子(处理y和x的孩子的关系)
	if (y != NULL)
	{
		x->l = y->r;
		if (y->r != NULL)
		{
			y->r->parent = x;
		}
		//y的父亲变成x的父亲(处理y和x的父亲的关系)
		y->parent = x->parent;
		if (x->parent == NULL)//如果x是根节点,旋转后需要更新root的指向
		{
			root = y;
		}
		else//如果x不是根节点,则需要判断x是x->parent的左孩子还是右孩子
		{
			if (x == x->parent->l)
			{
				x->parent->l = y;
			}
			else
			{
				x->parent->r = y;
			}
		}
		//y的右孩子是x,x的父亲是y(处理y和x的关系)
		y->r = x;
		x->parent = y;
	}	
	return root;
}

//变色函数
void change_color(RBTNode* s)
{
	s->color = (s->color == RED ? BLACK : RED);
}
//调整函数
RBTree fixup(RBTree root, RBTNode* s)
{
	RBTNode* p = s->parent;//s的父亲
	RBTNode* g = NULL;//s的爷爷
	RBTNode* u = NULL;//s的叔叔
	//如果父亲节点存在并且父亲是红色,则需要调整
	while (p && p->color == RED)
	{
		g = p->parent;
		if (p == g->r)//父亲是爷爷的右孩子
		{
			u = g->l;//则叔叔是爷爷的左孩子
			//如果叔叔是红色
			if (u != NULL && u->color == RED)
			{
				//红叔长辈染,爷爷变新点
				change_color(g);
				change_color(p);
				change_color(u);
				s = g;
				p = s->parent;
			}
			else//运行到这里无论叔叔存不存在,都是黑色
			{
				//由于RL比RR多了一步,因此先判断是否是RL,然后统一执行RR操作
				if (s == p->l)
				{
					//先右旋,然后交换s和p两个指针
					root = right_rotate(root, p);
					swap(s, p);
				}
				//如果是RR,则直接执行这里
				//黑叔要旋转,代码要统一,两遍变一遍,还是父爷染
				change_color(g);
				change_color(p);
				root = left_rotate(root, g);
			}
		}
		else//父亲是爷爷的左孩子
		{
			u = g->r; //则叔叔是爷爷的右孩子
			//如果叔叔是红色
			if (u != NULL && u->color == RED)
			{
				//红叔长辈染,爷爷变新点
				change_color(g);
				change_color(p);
				change_color(u);
				s = g;
				p = s->parent;
			}
			else//运行到这里无论叔叔存不存在,都是黑色
			{
				//由于LR比LL多了一步,因此先判断是否是LR,然后统一执行LL操作
				if (s == p->r)
				{
					root = left_rotate(root, p);
					swap(s, p);
				}
				//如果是LL,则直接执行这里
				//黑叔要旋转,代码要统一,两遍变一遍,还是父爷染
				change_color(g);
				change_color(p);
				root = right_rotate(root, g);
			}
		}
	}
	//循环调整之后记得将根节点恢复成黑色
	root->color = BLACK;
	return root;
}

//插入节点
RBTree insertRBT(RBTree root, int key)
{
	//如果树为空
	if (root == NULL)
	{
		root = creatNode(key, NULL, NULL, NULL);
		root->color = BLACK;//根节点为黑色
		return root;
	}
	//如果树不为空->执行BST插入操作
	RBTNode* x = root;//记录插入位置
	RBTNode* xp = NULL;//记录插入位置的父亲节点
	//寻找插入位置
	while (x != NULL)
	{
		xp = x;
		if (key < x->data)
		{
			x = x->l;
		}
		else
		{
			x = x->r;
		}
	}
	RBTNode* s = creatNode(key, xp, NULL, NULL);
	//更新插入节点父亲的孩子信息
	if (key < xp->data)
	{
		xp->l = s;
	}
	else
	{
		xp->r = s;
	}
	root = fixup(root, s);
	return root;
}
//验证是否是红黑树(根据中序遍历+颜色进行判断)
void inorder_RBT(RBTNode* root)
{
	if (root != NULL)
	{
		inorder_RBT(root->l);
		cout << root->data << " " << root->color << " ";
		if (root->parent != NULL)
		{
			cout << root->parent->data << endl;
		}
		else
		{
			cout << "root" << endl;
		}
		inorder_RBT(root->r);
	}
}

//查找值为key的节点
RBTree findBST(RBTree root, int key)
{
	if (root == NULL || root->data == key)
	{
		return root;
	}
	if (root->data < key)
	{
		return findBST(root->r, key);
	}
	else //(root->data > key)
	{
		return findBST(root->l, key);
	}
}

//删除以root为根的红黑树删除元素值为key所在的节点k
//删除节点k后的调整函数
RBTree delete_fixup(RBTree root, RBTNode* x, RBTNode* p)//x为p的孩子
{
	RBTNode* w = NULL;//x的兄弟节点w
	//删除k后x为双黑节点(Ⅲ)
	while (x == NULL 
			|| (x != NULL && x->color == BLACK)
			&& x != root)
	{
		//Ⅲ.(1)//x右,w左
		if (x == p->r)
		{
			w = p->l;
			//(c)w红色,x黑色
			if (w != NULL && w->color == RED)
			{
				//兄父变色,朝双黑x旋转
				change_color(w);
				change_color(p);
				root = right_rotate(root, p);
				w = p->l;
			}//此时w已经变成黑色,转化成了(a)或者(b)
			//(a)w黑色,w孩子也都是黑色
			if ((w->l == NULL || (w->l != NULL && w->l->color == BLACK))//左孩子为黑色
				&& (w->r == NULL || (w->r != NULL && w->r->color == BLACK))//右孩子为黑色
				)
			{
				//兄弟变色,双黑上移
				change_color(w);
				x = p;
				p = x->parent;
			}
			else//(b)w黑色,w孩子至少有一个为红色
			{
				//LR(w是p的左孩子,w的右孩子为红色)
				//根据左孩子为黑进行判断,这样w两个孩子都为红色这种情况被归为LL型
				if (w->l == NULL
					|| (w->l != NULL && w->l->color == BLACK)
					)
				{
					//先将w和r(也就是w->r)变色,再以w为中心左旋,旋转之后将w作为p的左孩子(w = p->l)
					change_color(w);
					change_color(w->r);
					root = left_rotate(root, w);
					w = p->l;
				}
				else//LL(w左孩子为红色或者两个孩子都是红色)
				{
					//变色规则:r(w->l)变w,w变p,p变黑,然后以p为中心右旋
					w->l->color = w->color;
					w->color = p->color;
					p->color = BLACK;
					right_rotate(root, p);
				}
			}
		}
		else//Ⅲ.(2)//x左,w右
		{
			w = p->r;
			//(c)w红色,x黑色
			if (w != NULL && w->color == RED)
			{
				//兄父变色,朝双黑x旋转
				change_color(w);
				change_color(p);
				root = left_rotate(root, p);
				w = p->r;
			}//此时w已经变成黑色,转化成了(a)或者(b)
			//(a)w黑色,w孩子也都是黑色
			if ((w->l == NULL || (w->l != NULL && w->l->color == BLACK))//左孩子为黑色
				&& (w->r == NULL || (w->r != NULL && w->r->color == BLACK))//右孩子为黑色
				)
			{
				//兄弟变色,双黑上移
				change_color(w);
				x = p;
				p = x->parent;
			}
			else//(b)w黑色,w孩子至少有一个为红色
			{
				//RL(w是p的右孩子,w的左孩子为红色)
				//根据右孩子为黑进行判断,这样w两个孩子都为红色这种情况被归为RR型
				if (w->r == NULL
					|| (w->r != NULL && w->r->color == BLACK)
					)
				{
					//先将w和r(也就是w->l)变色,再以w为中心左旋,旋转之后将w作为p的右孩子(w = p->r)
					change_color(w);
					change_color(w->l);
					root = right_rotate(root, w);
					w = p->r;
				}
				else//RR(w右孩子为红色或者两个孩子都是红色)
				{
					//变色规则:r(w->r)变w,w变p,p变黑,然后以p为中心左旋
					w->r->color = w->color;
					w->color = p->color;
					p->color = BLACK;
					left_rotate(root, p);
				}
			}
		}
	}
	//Ⅱ.
	if (x != NULL && x->color == RED)
	{
		change_color(x);
	}
	return root;
}
//删除k节点
RBTree deleteRBT(RBTree root, RBTNode* k)
{
	//度为2的节点
	if (k->l != NULL && k->r != NULL)
	{
		//找中序遍历前驱或者后继节点来替换(这里以后继为例)
		RBTNode* replace = k->r;
		while (replace->l)
		{
			replace = replace->l;
		}
		k->data = replace->data;
		k = replace;//转化成了度为1或者0的节点
	}
	//度为1或者0的节点
	RBTNode* p = k->parent;
	RBTNode* child = NULL;
	//child取代k的位置
	if (k->l != NULL)//k的左孩子存在
	{
		child = k->l;
	}
	else//k的右孩子存在或者k没有孩子
	{
		child = k->r;
	}
	if (p != NULL)//k的父亲存在,child替换k的位置
	{
		if (k == p->l)
		{
			p->l = child;
		}
		else
		{
			p->r = child;
		}
	}
	else//k的父亲不存在(k为根节点),child变成新的根节点
	{
		root = child;
	}
	if (child != NULL)//如果child不是空节点,则建立child和p的关系
	{
		child->parent = p;
	}
	int color = k->color;
	free(k);
	k = NULL;
	if (color == BLACK)//只有k为黑色的时候才需要调整
	{
		root = delete_fixup(root, child, p);
	}
	return root;
}
RBTree delete_x(RBTree root, int key)
{
	RBTNode* k = findBST(root, key);//先找到待删除的节点
	if (k != NULL)//只有红黑树中存在该节点才进行删除
	{
		root = deleteRBT(root, k);
	}
	return root;
}

int main()
{
	int n;
	int a[105];
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}
	RBTree root = NULL;
	for (int i = 1; i <= n; i++)
	{
		root = insertRBT(root, a[i]);
	}
	inorder_RBT(root);
	//删除元素值为x的节点
	int x;
	cin >> x;
	root = delete_x(root, x);
	inorder_RBT(root);
	return 0;
}
/*
样例输入:
9
10 40 30 60 90 70 20 50 80
60
正确输出:
10 1 30
20 0 10
30 1 root
40 1 60
50 0 40
60 0 30
70 0 80
80 1 60
90 0 80

10 1 30
20 0 10
30 1 root
40 1 70
50 0 40
70 0 30
80 1 70
90 0 80
*/
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值