红黑树实现


前言

代码基于胡船长的思维实现,感谢胡船长的细心讲解。
红黑树属于二叉排序树, 用于动态的插入,删除,查找。
红黑树的五条基本性质:

  1. 每个节点非黑即红
  2. 根节点为黑色
  3. 叶子节点为黑色
  4. 红色节点下面必须是黑色节点
  5. 每条路径上黑色节点数目相等

以下关于红黑树的讨论将紧扣这五条基本性质

一, 函数调用流程及插入删除总结

函数调用流程及函数主要功能描述:

在这里插入图片描述
插入总结:
在这里插入图片描述删除总结:
在这里插入图片描述

二, 最终代码实现

先放上最终代码实现, 之后有详细说明

#include <iostream>
#define red 0
#define black 1
#define double_black 2
using namespace std;

typedef struct node{
	int val, color;
	struct node *L, *R;
}node;

node temp_node = {0, 1, 0, 0}, *NIL = &temp_node;

void output(node *root){
	if(root == NIL) return ;
	output(root->L);
	printf("%d\n", root->val);
	output(root->R);
}

bool is_red(node *root){
	return root->color == red;
}

node *new_node(int x){
	node *temp = (node *)calloc(1, sizeof(node));
	temp->val = x;
	temp->L = temp->R = NIL;
	return temp;
}

node *L_move(node *root){
	node *temp = root->R;
	root->R = temp->L;
	temp->L = root;
	return temp;
}

node *R_move(node *root){
	node *temp = root->L;
	root->L = temp->R;
	temp->R = root;
	return temp;
}

node *insert_maintain(node *root){
	if(root == NIL) return root;

	if(is_red(root->L) && is_red(root->R)){
		root->L->color = root->R->color = black;
		root->color = red;
	}else if(is_red(root->L)){
		if(is_red(root->L->R)){
			root->L = L_move(root->L);
		}
		if(is_red(root->L->L)){
			root = R_move(root);
			swap(root->color, root->R->color);
		}
	}else if(is_red(root->R)){
		if(is_red(root->R->L)){
			root->R = R_move(root->R);
		}
		if(is_red(root->R->R)){
			root = L_move(root);
			swap(root->color, root->L->color);
		}
	}

	return root;
}

node *insert_node(node *root, int x){
	if(root == NIL) return new_node(x);

	if(root->val > x){
		root->L = insert_node(root->L, x);
	}else if(root->val < x){
		root->R = insert_node(root->R, x);
	}

	return insert_maintain(root);
}

node *insert(node *root, int x){
	root = insert_node(root, x);
	root->color = black;
	return root;
}

node *find_node(node *root){
	root = root->L;
	while(root->R != NIL) root = root->R;
	return root;
}

node *del_maintain(node *root){
	if(root == NIL) return root;

	if(root->L->color == double_black){
		if(is_red(root->R)){
			root = L_move(root);
			swap(root->color, root->L->color);
			root->L = del_maintain(root->L);
			return root;
		}
		if(!is_red(root->R->R)){
			if(!is_red(root->R->L)){
				root->L->color = black;
				root->color += 1;
				root->R->color = red;
				return root;
			}
			root->R = R_move(root->R);
			swap(root->R->color, root->R->R->color);
		}
		root->L->color = black;
		root = L_move(root);
		root->color = root->L->color;
		root->L->color = root->R->color = black;
	}else if(root->R->color == double_black){
		if(is_red(root->L)){
			root = R_move(root);
			swap(root->color, root->R->color);
			root->R = del_maintain(root->R);
			return root;
		}
		if(!is_red(root->L->L)){
			if(!is_red(root->L->R)){
				root->R->color = black;
				root->color += 1;
				root->L->color = red;
				return root;
			}
			root->L = L_move(root->L);
			swap(root->L->color, root->L->L->color);
		}
		root->R->color = black;
		root = R_move(root);
		root->color = root->R->color;
		root->R->color = root->L->color = black;
	}

	return root;
}

node *del_node(node *root, int x){
	if(root == NIL) return root;

	if(root->val > x){
		root->L = del_node(root->L, x);
	}else if(root->val < x){
		root->R = del_node(root->R, x);
	}else{
		node *temp = root;
		if(root->L != NIL && root->R != NIL){
			temp = find_node(root);
			root->val = temp->val;
			root->L = del_node(root->L, temp->val);
		}else{
			if(root->L != NIL){
				root = root->L;
			}else{
				root = root->R;
			}
			root->color += temp->color;
			free(temp);
		}
	}

	return del_maintain(root);
}

node *del(node *root, int x){
	root = del_node(root, x);
	root->color = black;
	return root;
}

void clear(node *root){
	if(root == NIL) return ;
	clear(root->L);
	clear(root->R);
	free(root);
}

int main(){
	int val, n;
	node *root = NIL;
	cin >> n;
	for(int i=0; i<n; ++i){
		cin >> val;
		root = insert(root, val);
//		cout << "insert: " << val << endl;
//		output(root);
//		cout << "-------------------" << endl << endl;
	}

	for(int i=0; i<n; ++i){
		cin >> val;
		root = del(root, val);
//		cout << "delete: " << val << endl;
//		output(root);
//		cout << "-------------------" << endl << endl;
	}
	clear(root);
	return 0;
}

三, 二叉排序树的实现

二叉排序树具有左子树的所有结点小于根节点, 右子树的所有结点大于根节点的性质, 二叉排序树结点的插入将基于这个性质。

1 ,二叉排序树的节点插入:

node *insert_node(node *root, int x){
	if(root == NIL) return new_node(x);

	if(root->val > x){
		root->L = insert_node(root->L, x);
	}else if(root->val < x){
		root->R = insert_node(root->R, x);
	}

	return root;
}

递归插入, 如果待插入的值小于根节点的值则递归在左子树中插入, 如果待插入的值大于根节点的值, 则递归在右子树中插入。

2,二叉排序树的节点删除:

(1)如果待删除节点没有左右子孩子

在这里插入图片描述(NIL节点表示该节点的相应孩子为空, 图中的问号表示该位置节点个数和结构不确定, 可能有多个节点, 也可能没有节点。)

直接将NIL节点挂到待删除节点的位置

(2) 待删除节点只有左孩子

在这里插入图片描述将唯一左孩子挂到待删除节点位置

(3) 待删除节点只有右孩子

与待删除节点只有左孩子的操作对称

(4) 待删除节点有左右两个孩子

在这里插入图片描述

找到所有小于该节点的节点中最大的一个节点(前驱节点), 将前驱节点挂在当前位置, 删除前驱节点, 可以证明前驱节点一定没有右孩子(如果有右孩子, 那么我们找到的就不是所有小于该节点的节点中最大的一个节点了), 删除前驱节点就变成了删除没有左右子孩子或者只有唯一的左孩子的节点了, 而这两种情况前面已经讨论过了。

node *find_node(node *root){  //找前驱节点
	root = root->L;
	while(root->R != NIL) root = root->R;
	return root;
}

node *del_node(node *root, int x){
	node *temp = find_node(root);
	root->val = temp->val;
	删除前驱节点
}

(5)删除节点的代码实现

node *find_node(node *root){  //找前驱节点
	root = root->L;
	while(root->R != NIL) root = root->R;
	return root;
}

node *del_node(node *root, int x){
	if(root == NIL) return root;

	if(root->val > x){
		root->L = del_node(root->L, x);
	}else if(root->val < x){
		root->R = del_node(root->R, x);
	}else{    //找到了待删除的节点
		node *temp = root;
		if(root->L != NIL && root->R != NIL){ //待删除节点有左右两个子孩子
			temp = find_node(root);
			root->val = temp->val;
			root->L = del_node(root->L, temp->val);
		}else{
			if(root->L != NIL){  //待删除节点只有左孩子
				root = root->L;
			}else{   //待删除节点只有右孩子或没有子孩子
				root = root->R;
			}
			free(temp);
		}
	}

	return root;
}

3,二叉排序树插入与删除节点的代码实现

#include <iostream>
using namespace std;

typedef struct node{
	int val;
	struct node *L, *R;
}node;

node temp_node = {0, 0, 0}, *NIL = &temp_node;

node *new_node(int x){
	node *temp = (node *)calloc(1, sizeof(node));
	temp->val = x;
	temp->L = temp->R = NIL;
	return temp;
}

node *insert_node(node *root, int x){
	if(root == NIL) return new_node(x);

	if(root->val > x){
		root->L = insert_node(root->L, x);
	}else if(root->val < x){
		root->R = insert_node(root->R, x);
	}

	return root;
}

node *insert(node *root, int x){
	root = insert_node(root, x);
	return root;
}

node *find_node(node *root){
	root = root->L;
	while(root->R != NIL) root = root->R;
	return root;
}

node *del_node(node *root, int x){
	if(root == NIL) return root;

	if(root->val > x){
		root->L = del_node(root->L, x);
	}else if(root->val < x){
		root->R = del_node(root->R, x);
	}else{
		node *temp = root;
		if(root->L != NIL && root->R != NIL){
			temp = find_node(root);
			root->val = temp->val;
			root->L = del_node(root->L, temp->val);
		}else{
			if(root->L != NIL){
				root = root->L;
			}else{
				root = root->R;
			}
			free(temp);
		}
	}

	return root;
}

node *del(node *root, int x){
	root = del_node(root, x);
	return root;
}

四, 由二叉排序树到红黑树

一, 加入记录颜色的元素

#include <iostream>
#define red 0
#define black 1
#define double_black 2  //删除节点后为了维护红黑树五条基本性质而短暂存在
using namespace std;

typedef struct node{
	int val, color;
	struct node *L, *R;
}node;

node temp_node = {0, black, 0, 0}, *NIL = &temp_node;  //叶子节点为黑色

二, 插入节点后对红黑树五条基本性质的维护

1. 插入节点的颜色

在这里插入图片描述

按照红黑树五条基本性质, 每条路径上黑色节点数相等, 虽然问号区域内的情况不确定, 但在研究的区域内(即红线圈出的区域)左右路径都有一个黑色节点, 如果插入黑色节点一定会导致红黑树失衡, 由此可见, 要插入的节点的颜色一定要是红色。

2. 左旋和右旋

因为在插入, 删除,和调整过程中都可能导致红黑树失衡, 此时为了维护红黑树的五条基本性质, 会进行调整, 调整过程中涉及左旋和右旋, 所以在这里先介绍左旋和右旋的相关操作。
在这里插入图片描述左旋和右旋的代码实现:

node *L_move(node *root){
	node *temp = root->R;
	root->R = temp->L;
	temp->L = root;
	return temp;
}

node *R_move(node *root){
	node *temp = root->L;
	root->L = temp->R;
	temp->R = root;
	return temp;
}

3. 插入和调整节点过程中导致失衡的情况

插入的节点为红色, 如果插入到黑色节点下面不违反红黑树的五条基本性质, 试想在某一次插入过程中违反了红黑树的五条基本性质之一, 那只可能是插入到了红色的节点下面(违反了红色节点下面必须是黑色节点的性质), 调整过程中也可能导致红色节点下面接红色节点的情况, 下面将对这种情况的各种可能情况进行讨论。

情况一

在这里插入图片描述

结点的左右子孩子都是红色, 子孩子的子孩子出现红色, 此时违背红黑树的五条性质之一(红色结点下必须是黑色节点), 其实确定这个状态只需要两个孩子节点是红色, 孩子孩子节点有一个红色结点, 这三个点就够了, 但是为了说明在调整过程中不会同时出现多个,红色结点下面还是红色结点的情况,画上了一些其它的结点。
图中相关的树形结构都只是更大红黑树的一部分, 也就是上面,下面都可能还有结点,图中画出的只是我们研究的部分, 之后的图都是一样的。
相关结点下面的数字表示在画出的结构中它的上面应该有的黑色结点个数, 这个数字是由初始状态(也就是最前面的一个图确定的, 在之后的图中, 可能会有相关数字跟上面黑色结点数并不对应的情况, 这是因为调整过程中出现了失衡, 违背了每条路径上黑色结点个数不相等的情况, 我么调整的策略就是要让调整后的数字与上面的黑色结点数相对应), 为了更好说明, 再附上一张图:
在这里插入图片描述第一张图是插入或者调整过程中出现失衡, 违背红色结点下面必须是黑色结点的性质, 但对于整颗红黑树来说, 每条路径上黑色结点数目还是相等的, 问号区域代表结构未知, 写上的数字1, 1, 2代表红黑树每条路径上黑色结点数目相等的情况下, 红圈圈出的区域内上面应该有的黑色结点数, 进行一个右旋(前面介绍了右旋和左旋的相关操作)调整后, 不仅违反了红色结点下面必须是黑色结点的性质, 而且还导致了红黑树的所有路径中出现了黑色结点个数不相等的路径(不相等是因为初始状态, 也就是第一张图中相关路径的黑色结点数目是相等的, 但右旋后红色区域内相关路径上的黑色结点数目发生了改变), 之后进行一轮颜色调整, 目的是为了去掉红色结点下面为红色结点的情况, 同时要使红色区域内相关路径上的黑色结点数目与初始状态相同(维护每条路径上黑色结点数目相等的性质)。
之后介绍的各种类型的分析情况跟这个分析是类似的。

情况二

在这里插入图片描述结点有红色子孩子和黑色子孩子, 并且红色子孩子下面有红色子孩子, 按照红色子孩子的红色子孩子是左结点还是右结点, 分为两种类型, 第一种类型如图所示, 先进行右旋, 然后进行颜色调整, 第二种类型如图所示, 先对红色子孩子进行左旋, 就变成了第一种类型, 接下来按照第一种类型的调整策略。
图中只画出了红色结点在左边的情况, 红色结点在右边的情况的操作与分析都是对称的。

4,插入调整总结

在这里插入图片描述

5,插入调整代码实现

插入调整是在二叉排序树插入结点后进行的调整, 所以只是在二叉排序树上增加了调整平衡的功能

bool is_red(node *root){
	return root->color == red;
}

node *new_node(int x){
	node *temp = (node *)calloc(1, sizeof(node));
	temp->val = x;
	temp->L = temp->R = NIL;
	return temp;
}

node *L_move(node *root){
	node *temp = root->R;
	root->R = temp->L;
	temp->L = root;
	return temp;
}

node *R_move(node *root){
	node *temp = root->L;
	root->L = temp->R;
	temp->R = root;
	return temp;
}

node *insert_maintain(node *root){
	if(root == NIL) return root;

	if(is_red(root->L) && is_red(root->R)){
		root->L->color = root->R->color = black;
		root->color = red;
	}else if(is_red(root->L)){
		if(is_red(root->L->R)){
			root->L = L_move(root->L);
		}
		if(is_red(root->L->L)){
			root = R_move(root);
			swap(root->color, root->R->color);
		}
	}else if(is_red(root->R)){
		if(is_red(root->R->L)){
			root->R = R_move(root->R);
		}
		if(is_red(root->R->R)){
			root = L_move(root);
			swap(root->color, root->L->color);
		}
	}

	return root;
}

node *insert_node(node *root, int x){
	if(root == NIL) return new_node(x);

	if(root->val > x){
		root->L = insert_node(root->L, x);
	}else if(root->val < x){
		root->R = insert_node(root->R, x);
	}

	return insert_maintain(root);
}

node *insert(node *root, int x){
	root = insert_node(root, x);
	root->color = black;        //保证根节点为黑
	return root;
}

三,删除结点后对红黑树五条基本性质的维护

一,删除结点

与插入结点不同, 删除的结点可能为黑色, 会破坏红黑树的五条基本性质之一的每条路径上黑色结点树相等, 为了解决这个问题, 引入一个临时的颜色, 它比黑色还黑, 不妨叫做双倍黑(double black), 以下还是分情况讨论。

1, 待删除的结点没有左右子孩子(double black的产生)

在这里插入图片描述
老规矩, 我们只研究红圈内的部分, 图一删除前红圈内每条路径上面有一个黑色结点, 删除后并不影响。
图二删除前红圈内每条路径上有两个黑色结点, 删除后每条路径上只有一个黑色结点了, 违背了红黑树每条路径上黑色结点数相等的原则, 于是我们将这个结点的颜色标记为双倍黑, 虽然只有一个结点, 但双倍黑的颜色使得我们可以把它看成是两个黑色结点的组合, 思维上当作两个黑色结点, 这样就不违背红黑树每条路径上黑色结点数必须相等的原则了。

2, 待删除结点有一个孩子

在这里插入图片描述

如果待删除结点只有一个子孩子, 那么他的颜色及它子孩子的颜色都是可以确定的, 因为它只有一个子孩子, 所以另一个结点一定为NIL结点, NIL结点为黑色, 可以确定另一条路径上没有除NIL的其它黑色结点了,而已经确定该节点有一个子孩子, 子孩子不能为黑色, 那就只能是红色, 子孩子一但为红色, 那该节点就不能为红色了, 只能为黑色。
对这种情况的相关操作如图所示。

3, 待删除结点有两个子孩子

与二叉排序树的操作是一样的, 找到前驱结点,用前驱结点的值替换待删除结点的值, 转而删除前驱结点。

二,删除和调整过程中导致失衡的情况

1,结点左右子孩子一个为double black, 另一个为红色

在这里插入图片描述
以图中的为例, 图中双倍黑的结点在左边, 右边为红色结点, 所以对整体进行一个左旋, 然后进行颜色调整, 然后双倍黑色的结点出现在左孩子的左孩子上了, 这就变成了下面介绍的另一种情况了。
为什么这么调整, 首先图中画出的结点只属于红黑树的一部分, 数字表示在红黑树平衡的情况下,图中相关路径上面应该存在的黑色结点个数, 第一张图是在红黑树平衡的情况下图中每条路径应该有的黑色结点个数, 经过左旋后显然相关路径上黑色结点数目变化了(注意, 相关数字只表示红黑树平衡时该路径上面应该有的黑色结点个数), 此时红黑树变得不平衡了, 然后经过图中的颜色调整, 红黑树就平衡了。
图中只画出了双倍黑结点在左边的情况, 双倍黑结点在右边的操作是对称的。

2, 结点左右子孩子一个为double black,另一个为黑色
(1)黑色孩子的左右子孩子都为黑色

在这里插入图片描述
白色表示颜色不确定
这种调整方式比较简单, 给左右子孩子的颜色都减一, 当前结点颜色加一。
这么调整的原因与情况一是类似的。

(2)黑色孩子内侧子孩子为红色, 外侧子孩子为黑色

在这里插入图片描述

白色表示颜色不确定, 2+表示黑色结点由未确定的结点决定, 至少为2
左孩子是双倍黑色, 右孩子是黑色, 右孩子的左孩子为红色(内侧结点), 右孩子的右孩子为黑色(外侧结点), 这种情况先对右孩子进行一个右旋, 然后进行一轮颜色调整, 就变成了下面要介绍的情况了。
为什么要这么调整颜色参考之前的介绍, 思想都是类似的。
右孩子是双倍黑色, 左孩子是黑色, 左孩子的右孩子为红色(内侧结点),左孩子的左孩子为黑色(外侧结点), 这种情况与图中的操作是对称的。

(3) 黑色孩子的外侧子孩子为红色

在这里插入图片描述白色表示颜色不确定
左孩子为双倍黑色, 右孩子为黑色, 右孩子的右孩子(外侧结点)为红色, 这种情况先整体进进行左旋, 然后进行一轮颜色调整, 相关调整如图所示, 为什么这样调整颜色参考前面的介绍, 相关思想类似的。
右孩子为双倍黑色, 左孩子为黑色, 左孩子的左孩子(外侧结点)为红色,这种情况的调整与图中的操作是对称的。

三,删除结点总结

在这里插入图片描述

四,删除结点代码实现

红黑树删除结点的代码只是在二叉排序树删除结点的基础上增加了维护结点颜色的功能。

bool is_red(node *root){
	return root->color == red;
}

node *L_move(node *root){
	node *temp = root->R;
	root->R = temp->L;
	temp->L = root;
	return temp;
}

node *R_move(node *root){
	node *temp = root->L;
	root->L = temp->R;
	temp->R = root;
	return temp;
}

node *find_node(node *root){
	root = root->L;
	while(root->R != NIL) root = root->R;
	return root;
}

node *del_maintain(node *root){
	if(root == NIL) return root;

	if(root->L->color == double_black){
		if(is_red(root->R)){
			root = L_move(root);
			swap(root->color, root->L->color);
			root->L = del_maintain(root->L);
			return root;
		}
		if(!is_red(root->R->R)){
			if(!is_red(root->R->L)){
				root->L->color = black;
				root->color += 1;
				root->R->color = red;
				return root;
			}
			root->R = R_move(root->R);
			swap(root->R->color, root->R->R->color);
		}
		root->L->color = black;
		root = L_move(root);
		root->color = root->L->color;
		root->L->color = root->R->color = black;
	}else if(root->R->color == double_black){
		if(is_red(root->L)){
			root = R_move(root);
			swap(root->color, root->R->color);
			root->R = del_maintain(root->R);
			return root;
		}
		if(!is_red(root->L->L)){
			if(!is_red(root->L->R)){
				root->R->color = black;
				root->color += 1;
				root->L->color = red;
				return root;
			}
			root->L = L_move(root->L);
			swap(root->L->color, root->L->L->color);
		}
		root->R->color = black;
		root = R_move(root);
		root->color = root->R->color;
		root->R->color = root->L->color = black;
	}

	return root;
}

node *del_node(node *root, int x){
	if(root == NIL) return root;

	if(root->val > x){
		root->L = del_node(root->L, x);
	}else if(root->val < x){
		root->R = del_node(root->R, x);
	}else{
		node *temp = root;
		if(root->L != NIL && root->R != NIL){
			temp = find_node(root);
			root->val = temp->val;
			root->L = del_node(root->L, temp->val);
		}else{
			if(root->L != NIL){
				root = root->L;
			}else{
				root = root->R;
			}
			root->color += temp->color;
			free(temp);
		}
	}

	return del_maintain(root);
}

node *del(node *root, int x){
	root = del_node(root, x);
	root->color = black;
	return root;
}

五, 代码检验

代码检验还是要从红黑树的五条基本性质入手, 同时红黑树属于二叉排序树, 如果我们的红黑树同时满足红黑树五条基本性质(每个结点非黑即红, 根结点为黑色, 叶子结点为红色, 红色结点下面必须为黑色结点, 每条路径上的黑色结点树相等)和二叉排序树基本性质(根结点的值大于左子树的值, 根结点的值小于右子树的值), 那么它应该是没有问题的。
于是我们实现一个校验函数
我们首先实现一个随机程序, 该程序随机测试数据

#include <iostream>
#include <vector>
#include <time.h>
#define N 10000
using namespace std;

int main(){
	cout << N << endl;
	vector<int> data(N);
	for(int i=0; i<N; ++i) data[i] = i - 100;

	srand(time(0));
	{       //随机插入的数据
		for(int cnt=0; cnt<N; ++cnt){
			int i = rand() % N;
			int j = rand() % N;
			swap(data[i], data[j]);
		}

		for(int i: data) cout << i << " ";
		cout << endl;
	}
	{        //随机删除的数据
		for(int cnt=0; cnt<N; ++cnt){
			int i = rand() % N;
			int j = rand() % N;
			swap(data[i], data[j]);
		}

		for(int i: data) cout << i << " ";
		cout << endl;
	}
	return 0;
}

然后我们实现测试代码

bool judge_node(node *root, int cnt, int *total){
	if(root == NIL){
		if(*total == 0){
			*total = cnt;
		}
		return *total == cnt;
	}

	if(root->color == red){
		if(root->L->color == red || root->R->color == red){
			cout << "node 颜色是为红, node 孩子也为红!" << endl;
			return false;
		}
	}else if(root->color != black){
		cout << "node 颜色不是红色或着黑色!" << endl;
		return false;
	}

	if(root->L != NIL && root->R != NIL){
		if(root->val <= root->L->val){
			cout << "left >= root !" << endl;
			return false;
		}
		if(root->val >= root->R->val){
			cout << "right <= root !" << endl;
			return false;
		}
	}
	
	bool sign = true;
	if(root->color == black){
		sign = judge_node(root->L, cnt+1, total);
		if(sign == false) return false;
		sign = judge_node(root->R, cnt+1, total);
	}else{
		sign = judge_node(root->L, cnt, total);
		if(sign == false) return false;
		sign = judge_node(root->R, cnt, total);
	}

	return sign;
}

bool judge(node *root){
	if(root->color != black) {
		cout << "root 颜色不是黑色!" << endl;
		return false;
	}
	if(NIL->color != black) {
		cout << "NIL 颜色不是黑色!" << endl;
		return false;
	}
	int total = 0;
	return judge_node(root, 0, &total);
}

将测试代码放入红黑树程序中, 测试是否正确

#include <iostream>
#define red 0
#define black 1
#define double_black 2
using namespace std;

typedef struct node{
	int val, color;
	struct node *L, *R;
}node;

node temp_node = {0, 1, 0, 0}, *NIL = &temp_node;

void output(node *root){
	if(root == NIL) return ;
	printf("%d [%d %d] %d\n", root->L->val, root->val, root->color, root->R->val);
	output(root->L);
	output(root->R);
}

bool is_red(node *root){
	return root->color == red;
}

node *new_node(int x){
	node *temp = (node *)calloc(1, sizeof(node));
	temp->val = x;
	temp->L = temp->R = NIL;
	return temp;
}

node *L_move(node *root){
	node *temp = root->R;
	root->R = temp->L;
	temp->L = root;
	return temp;
}

node *R_move(node *root){
	node *temp = root->L;
	root->L = temp->R;
	temp->R = root;
	return temp;
}

node *insert_maintain(node *root){
	if(root == NIL) return root;

	if(is_red(root->L) && is_red(root->R)){
		root->L->color = root->R->color = black;
		root->color = red;
	}else if(is_red(root->L)){
		if(is_red(root->L->R)){
			root->L = L_move(root->L);
		}
		if(is_red(root->L->L)){
			root = R_move(root);
			swap(root->color, root->R->color);
		}
	}else if(is_red(root->R)){
		if(is_red(root->R->L)){
			root->R = R_move(root->R);
		}
		if(is_red(root->R->R)){
			root = L_move(root);
			swap(root->color, root->L->color);
		}
	}

	return root;
}

node *insert_node(node *root, int x){
	if(root == NIL) return new_node(x);

	if(root->val > x){
		root->L = insert_node(root->L, x);
	}else if(root->val < x){
		root->R = insert_node(root->R, x);
	}

	return insert_maintain(root);
}

node *insert(node *root, int x){
	root = insert_node(root, x);
	root->color = black;
	return root;
}

node *find_node(node *root){
	root = root->L;
	while(root->R != NIL) root = root->R;
	return root;
}

node *del_maintain(node *root){
	if(root == NIL) return root;

	if(root->L->color == double_black){
		if(is_red(root->R)){
			root = L_move(root);
			swap(root->color, root->L->color);
			root->L = del_maintain(root->L);
			return root;
		}
		if(!is_red(root->R->R)){
			if(!is_red(root->R->L)){
				root->L->color = black;
				root->color += 1;
				root->R->color = red;
				return root;
			}
			root->R = R_move(root->R);
			swap(root->R->color, root->R->R->color);
		}
		root->L->color = black;
		root = L_move(root);
		root->color = root->L->color;
		root->L->color = root->R->color = black;
	}else if(root->R->color == double_black){
		if(is_red(root->L)){
			root = R_move(root);
			swap(root->color, root->R->color);
			root->R = del_maintain(root->R);
			return root;
		}
		if(!is_red(root->L->L)){
			if(!is_red(root->L->R)){
				root->R->color = black;
				root->color += 1;
				root->L->color = red;
				return root;
			}
			root->L = L_move(root->L);
			swap(root->L->color, root->L->L->color);
		}
		root->R->color = black;
		root = R_move(root);
		root->color = root->R->color;
		root->R->color = root->L->color = black;
	}

	return root;
}

node *del_node(node *root, int x){
	if(root == NIL) return root;

	if(root->val > x){
		root->L = del_node(root->L, x);
	}else if(root->val < x){
		root->R = del_node(root->R, x);
	}else{
		node *temp = root;
		if(root->L != NIL && root->R != NIL){
			temp = find_node(root);
			root->val = temp->val;
			root->L = del_node(root->L, temp->val);
		}else{
			if(root->L != NIL){
				root = root->L;
			}else{
				root = root->R;
			}
			root->color += temp->color;
			free(temp);
		}
	}

	return del_maintain(root);
}

node *del(node *root, int x){
	root = del_node(root, x);
	root->color = black;
	return root;
}

void clear(node *root){
	if(root == NIL) return ;
	clear(root->L);
	clear(root->R);
	free(root);
}

bool judge_node(node *root, int cnt, int *total){
	if(root == NIL){
		if(*total == 0){
			*total = cnt;
		}
		return *total == cnt;
	}

	if(root->color == red){
		if(root->L->color == red || root->R->color == red){
			cout << "node 颜色是为红, node 孩子也为红!" << endl;
			return false;
		}
	}else if(root->color != black){
		cout << "node 颜色不是红色或着黑色!" << endl;
		return false;
	}

	if(root->L != NIL && root->R != NIL){
		if(root->val <= root->L->val){
			cout << "left >= root !" << endl;
			return false;
		}
		if(root->val >= root->R->val){
			cout << "right <= root !" << endl;
			return false;
		}
	}
	
	bool sign = true;
	if(root->color == black){
		sign = judge_node(root->L, cnt+1, total);
		if(sign == false) return false;
		sign = judge_node(root->R, cnt+1, total);
	}else{
		sign = judge_node(root->L, cnt, total);
		if(sign == false) return false;
		sign = judge_node(root->R, cnt, total);
	}

	return sign;
}

bool judge(node *root){
	if(root->color != black) {
		cout << "root 颜色不是黑色!" << endl;
		return false;
	}
	if(NIL->color != black) {
		cout << "NIL 颜色不是黑色!" << endl;
		return false;
	}
	int total = 0;
	return judge_node(root, 0, &total);
}

int main(){
	int val, n;
	node *root = NIL;
	bool sign = true;;
	cin >> n;
	for(int i=0; i<n; ++i){
		cin >> val;
		root = insert(root, val);
//		cout << "insert: " << val << endl;
//		output(root);
//		cout << "-------------------" << endl << endl;
		if(sign) sign = judge(root);
	}

	for(int i=0; i<n; ++i){
		cin >> val;
		root = del(root, val);
//		cout << "delete: " << val << endl;
//		output(root);
//		cout << "-------------------" << endl << endl;
		if(sign) sign = judge(root);
	}
	cout << (sign? "success": "fail") << endl;
	clear(root);
	return 0;
}

测试结果:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值