数据结构-----红黑树(全)

目录

前言

 一、什么是红黑树?

 二、为什么需要红黑树?(与AVL树对比)

 三、红黑树的特性

四、红黑树的储存结构

 五、节点旋转操作

左旋(Left Rotation)

右旋(Right Rotation) 

六、插入节点操作

1.插入的是空树

2.插入节点的key重新重复

3.插入节点的父节点是黑色

4.插入节点的父节点是红色

4.1父节点是祖父节点的左子节点

4.1.1叔叔节点是红色 

 4.1.2叔叔节点是黑色

4.1.2-1 插入节点是作左子节点

4.1.2-2插入节点是作右子节点

 4.2父节点是祖父节点的右子节点

4.2.1叔叔节点是红色

4.2.2 叔叔节点是黑色

4.2.1-1 插入节点是作左子节点

4.2.1-2 插入节点是作右子节点

七、红黑树的查找

八、红黑树的删除

1.删除的是叶子节点

1.1删除节点颜色为红色

1.2删除节点颜色为黑色

1.2-1 要删除节点D为黑色,兄弟节点没有左右孩子

1.2-2 要删除节点D为黑色,兄弟节点有左孩子,右孩子为空

1.2-3 要删除节点D为黑色,兄弟节点有右孩子,左孩子为空

1.2-4 要删除节点为黑色,兄弟节点左右孩子都存在,且为红色

 1.2-5 要删除节点为黑色,兄弟节点为红色

2.删除节点只有左孩子,没有右孩子

3.删除节点只有右孩子,没有左孩子 

 4.删除节点有左右子节点,且都为红色

 九、全部代码(C/C++)


前言

        当我们真正热爱这世界时,我们才真正生活在这世上。——泰戈尔

        前面我发布了三篇关于红黑树操作的博客,那这一篇就是对前三篇做一个汇总,前三篇的内容基本上都在本篇文章中,到这里红黑树的内容就全部结束了,以下就是红黑树的所有内容,各位看官请看!

前三篇链接

初识红黑树:数据结构-----红黑树简介-CSDN博客

红黑树的插入:数据结构-----红黑树的插入-CSDN博客

红黑树的删除:数据结构-----红黑树的删除操作-CSDN博客

 一、什么是红黑树?

        红黑树是一种自平衡的二叉查找树,是一种高效的查找树。它是由 Rudolf Bayer 于1972年发明,在当时被称为对称二叉 B 树(symmetric binary B-trees)。后来,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改为如今的红黑树。

红黑树如图所示: 

 二、为什么需要红黑树?(与AVL树对比)

在此之前我们学习了AVL树,既然AVL树有了高效率的查找功能,那需要红黑树干什么呢?下面看对比就知道了。

红黑树(Red-Black Tree)和AVL树(Adelson-Velsky and Landis Tree)都是自平衡二叉搜索树,用于在动态数据集上进行高效的插入、删除和搜索操作。它们之间有一些相似之处,但也存在一些关键的区别。如下所示:

平衡性比较:

AVL树:平衡二叉树是一种绝对平衡的二叉树,其满足每个节点的左右子树的高度只差不超过1,所以其在查找方面上是非常迅捷的,但是在插入和删除操作的时候要不断去旋转来满足平衡条件。

红黑树:红黑树是一种弱平衡的二叉树,其不需要像AVL树那样满足左右子树高度差不超过1,红黑树树的高度最多是2倍的对数级别,所以红黑树的插入和删除操作方面更具有灵活性,但是有一些方面性能还是不如AVL树的。

插入和删除性能比较:

AVL树:AVL树在插入和删除过程中必须满足绝对平衡,所以要频繁的进行旋转操作,时间复杂度比较大

红黑树:红黑树是满足弱平衡状态,有红黑两种颜色去控制树的结构,在插入和删除过程中不需要多次旋转操作,这方面是优于平衡二叉树的。

操作效率比较:

AVL树:平衡二叉树满足绝对平衡,其查找效率绝对是最快的,时间复杂度为 O(logn).

红黑树:虽然红黑树的查找时间复杂度也是O(logn),但是相较于平衡二叉树,操作速度是要慢一些的。

 

对比总结

AVL树适合应用于搜索场景,以查为主。

红黑树适合用于频繁插入、删除场景,其实用性更加强。

总的来说各有各的特色吧,现实生活和工作中用的比较多的方面那肯定是红黑树的了,所以学好红黑树很重要!!!

红黑树的相关应用场景:

红黑树具有良好的效率,它可在 O(logN) 时间内完成查找、增加、删除等操作。因此,红黑树在业界应用很广泛,比如 Java 中的 TreeMap,JDK 1.8 中的 HashMap、C++ STL 中的 map 均是基于红黑树结构实现的。  

 三、红黑树的特性

        既然知道了红黑树的优秀,多余的就不多说了,所以这里就开始学习红黑树的知识点了,首先先了解红黑树的特性,需要什么条件才可以满足红黑树。

对于一个红黑树必须满足以下的6个特性:

1.红黑树是一个二叉排序树

2.每个节点要么是红色,要么是黑色

3.根结点是黑色的

4.叶子节点(外部节点,NULL节点、失败的节点)都是黑色的

5.红色节点的父节点和子节点都是黑色的(不存在两个相邻的红色节点)

6.对于每一个节点,从该节点到任一叶子结点的路径上,其所含黑色节点的数量相同

红黑树上面这6条性质可能对于有些人不太好记住或者记错,别急,我下面送各位一个顺口溜,保证你们看了就懂: 

 顺口溜解释:

左根右:表示红黑树满足 左子节点<根节点<右子节点,也就是满足排序条件

根叶黑表示跟节点和叶子节点都是黑色的a

不红红表示不能有两个连续的红色节点(父节点和子节点不可能同时是红色的)

黑路同:表示从任意应该节点走到子节点路径上的黑色节点数量是相同的

 记住了这个顺口溜就等于记住了红黑树的特性,是不是很简单呢?来下面看几个简单的判断是否为红黑树的示例:

示例1: 

很明显这个不是红黑树,为什么呢?没有排序啊!!!

示例2:

 这个也不是红黑树,因为不满足 “不红红” 的特性。

示例3:

 这个也不是红黑树,可能有点不太好看,看到13->8->1->6 这条路径,发现有什么不同呢?很明显,这里不满足 “黑路同” 的性质,相较于其他路径这里多了一个黑色节点的数量。

四、红黑树的储存结构

 根据红黑树的要求,我们可以去定义红黑树节点和树的结构体,如下所示:

//宏定义颜色
#define red 0
#define black 1

//数据类型Datatype
typedef char Datatype;
//红黑树节点存储结构
typedef struct node {
	Datatype data;
	int color;
	int key;//排序键值,根据key大小排序
	struct node* par;//父节点指针
	struct node* left, * right;//左右子节点指针
}Node;

//红黑树的定义rbtree
typedef struct tree {
	Node* root;//指向根节点指针
	Node* nil;//叶子节点(哨兵)
}rbtree;

 五、节点旋转操作

在数据结构当中,旋转操作是一种很常见的操作,可能去实现数据结构平衡或者其他相关特性的要求,同样的的AVL树和红黑树里边也是要进行旋转操作的,通过旋转来满足平衡的特性。旋转分两种:左旋(Left Rotation)右旋(Right Rotation)

左旋(Left Rotation)

左旋是一种将某个节点的右子节点旋转上来的操作。也就是说当前节点的右子节点顶替了自己,然后自己变为右子节点的左子节点,以保持树的平衡。

操作如下:

  1. 将当前节点的右子节点设为新的父节点。
  2. 将新的父节点的左子节点设为当前节点的右子节点。
  3. 如果当前节点有父节点,将新的父节点替代当前节点的位置。
  4. 将当前节点设为新的父节点的左子节点。

 代码实现:

//左旋(以x为旋转点,向左旋转)
void left_rotate(rbtree* T, Node* x) {
	Node* y = x->right;//标记到右子节点
	x->right = y->left;//y的左子节点代替x的右子节点
	if (x->right != T->nil)
		x->right->par = x;//如果不为空(nil)其父节点指向x
	y->par = x->par;//把y的父节点指向x的父节点,此时x与y没有直接联系了
	if (x->par == T->nil) {//判断x的父节点是否为根结点
		T->root = y;//如果是的话,y就变为根结点
	}
	else {
		//y顶替x的位置
		if (x == x->par->left)
			x->par->left = y;//如果x是父节点的左边,那y就代替x成为左子节点
		else
			x->par->right = y;//如果x是父节点的右边,那y就代替x成为右子节点
	}
	//y的左子节点指向x,x的父节点指向y
	y->left = x;
	x->par = y;
}

右旋(Right Rotation) 

同样的右旋也是将左子节点顶替自己成为父节点, 然后自己成为左子节点的右子节点。

操作如下:

  1. 将当前节点的左子节点设为新的父节点
  2. 将新的父节点的右子节点设为当前节点的左子节点
  3. 如果当前节点有父节点,将新的父节点替代当前节点的位置
  4. 将当前节点设为新的父节点的右子节点

 代码实现:

//右旋(以x为旋转点,向右旋转)
void right_rotate(rbtree* T, Node* x) {
	Node* y = x->left;//标记到左子节点y
	x->left = y->right;//y的右子节点代替x的左子节点
	if (x->left != T->nil)
		x->left->par = x;
	y->par = x->par;//y的父节点指向x的父节点
	if (x->par == T->nil)
		T->root = y;//如果x是根结点的话,那么y代替x成为根结点
	else {
		if (x == x->par->left)
			x->par->left = y;
		else
			x->par->right = y;
	}
	//y的右子节点指向x,x的父节点为y
	y->right = x;
	x->par = y;
}

六、插入节点操作

再讲之前,我分享一个网址给大家(链接:Red/Black Tree Visualization),这个是一个红黑树模拟器的网址,你们可以去进行红黑树插入删除遍历等操作,可以自己试试看。如下图所示:

 废话不多说了,上正文!

红黑树的插入操作分两步走:

  • 找到插入位置
  • 进行自平衡调整

注意:插入节点初始为红色

原因分析:因为红黑树中任意一个节点到叶子节点路径所含黑色节点数量相同,也就是说如果我插入的节点为黑色的话,那么就会破坏红黑树的要求,所以插入的节点必须是红色节点,才能保证红黑树的性质。

下面就开始讨论红黑树的几种插入情况:

1.插入的是空树

这是最简单的插入情况,当插入第一个节点的时候,红黑树为空我们只需要让根节点指向这个节点即可。操作如下:

  1. 根节点指向此节点
  2. 把根节点染黑

2.插入节点的key重新重复

这种情况的话我们可以根据自己喜好去处理,如果出现了重复的key,那么就把这个key里面的值进行更新;或者我们不进行插入操作,因为key不可以重复,直接退出插入操作。

3.插入节点的父节点是黑色

这很好处理,直接插入就行了,因为父节点为黑色,插入节点为红色,所以不会影响红黑树的平衡性。

  1. 直接插入即可

4.插入节点的父节点是红色

这种情况是最为复杂的,由于父节点颜色是红色,所以要进行平衡调整,所以要去进一步的讨论才行。那具体根据什么去调整呢?是看叔叔节点的颜色来调整(父节点的兄弟节点),具体分以下几种情况:

 大的有两种情况,要看父节点是祖父节点的左边还是右边,下面我就以父节点为左子节点为例子:

 下文图标说明:

t 表示插入的节点

P表示父节点

B表示叔叔节点

PP表示祖父节点

4.1父节点是祖父节点的左子节点
4.1.1叔叔节点是红色 

如果叔叔节点的颜色是红色的话,这里不需要进行旋转操作,只需要让父节点和叔叔节点颜色变为黑色,祖父节点颜色变为红色即可。流程如下:

  • 把P 和B 节点染黑
  • 把PP节点染红

 4.1.2叔叔节点是黑色

这里的话又要去分两种情况:

  1. 插入节点是父节点的左子节点
  2. 插入节点是父节点的右子节点
4.1.2-1 插入节点是作左子节点

 如果插入的节点是父节点的左子节点的话,那么要进行以下操作:

  • 把P染黑
  • 把PP染红
  • 对PP进行右旋

4.1.2-2插入节点是作右子节点

 如果插入节点是作为父节点的右子节点的话,要进行以下操作:

  • 对P进行左旋
  • 把t 染黑
  • 把PP染红
  • 对PP进行右旋

 4.2父节点是祖父节点的右子节点

这里的操作跟4.1基本上是一模一样的,只是对称过去是了,但是我还是想详细列出来吧,下面接着看。

4.2.1叔叔节点是红色

操作步骤如下:

  • 把B(叔叔节点)和P(父节点)然黑
  • 把PP(祖父节点)染红

4.2.2 叔叔节点是黑色

同样的也是分以下两种情况讨论: 

4.2.1-1 插入节点是作左子节点
  •  对P 进行右旋
  • 将t 染黑
  • 将PP 然红
  • 对PP 进行左旋

4.2.1-2 插入节点是作右子节点
  •  将P 染黑
  • 将PP 然红
  • 对PP进行左旋

 以上这些就是红黑树的插入全部可能了,是不是很多啊,其实还好啦!只要我们把这些情况一个一个分类,然后思路捋一捋很容易弄明白的,后面讲到红黑树的删除还有更多种情况呢!还有就是,这些图片是我自己画的,呃画得不太好,不好意思哈。

七、红黑树的查找

红黑树是二叉排序树,查找也跟AVL树是一样的,根据key的值的大小去向左向右查找,找到就返回即可。代码如下:

//根据key查找
Node* Search_key(rbtree* T, int target) {
	assert(T);
	assert(T->root);
	Node* cur = T->root;
	while (cur) {
		if (cur->key == target)
			return cur;//找到就返回
		else if (cur->key > target)
			cur = cur->left;
		else
			cur = cur->right;
	}
	printf("The target is not exist\n");
	return NULL;
}

八、红黑树的删除

红黑树的删除所有情况如下所示:

  • 删除的是叶子节点(下面又分2种情况)
    • 删除节点的颜色是红色
    • 删除节点的颜色是黑色(下面再分5种情况)
      1. 兄弟节点没有左右孩子
      2. 兄弟节点左孩子为红色,右孩子为黑色
      3. 兄弟节点右孩子为红色,左孩子为黑色
      4. 兄弟节点有左右孩子,且都为红色
      5. 兄弟节点有左右孩子,且都为黑色(兄弟节点为红色)
  • 删除的只有左子节点,没有右子节点
  • 删除的只有右子节点,没有左子节点
  • 删除的既有左子节点,又有右子节点

 以上就是红黑树删除操作的全部情况,非常清晰,那这里就要去进行一个一个来讨论了。

以下图片标注说明

D:表示要删除的节点

P:表示删除节点的父节点

B:表示D的兄弟节点

LN:表示B的左子节点

RN:表示B的右子节点

1.删除的是叶子节点

如果删除的是叶子节点,那就要去看删除节点的颜色来操作,以下分两种情况:

  • 删除节点颜色为红色
  • 删除节点颜色为黑色

 注意事项

删除的是叶子结点,右两种可能,也就是要删除的叶子结点是左叶子结点或者是右叶子结点,下面我会去通过删除左叶子结点来去讨论上面这些过程,如果要删除右叶子结点,这里只需要进行对称操作就行了

1.1删除节点颜色为红色

 直接删除,因为删除掉红色节点不会影响到红黑树的基本特性

1.2删除节点颜色为黑色

如果要删除节点的颜色为黑色的话,那么这里就要考虑到被删除节点的兄弟节点的颜色了:

  • 如果兄弟节点颜色为黑色,那么父节点颜色既可以是黑色也可以是红色(下图用白色表示)
  • 如果兄弟节点颜色为红色,那么父节点颜色只能是黑色
1.2-1 要删除节点D为黑色,兄弟节点没有左右孩子

操作如下:

  • 删除D节点
  • P的颜色变为黑色
  • B的颜色变为红色

1.2-2 要删除节点D为黑色,兄弟节点有左孩子,右孩子为空

操作如下: 

  •  删除D节点
  • 对B进行右旋
  • LN的颜色变为P的颜色
  • P的颜色变为黑色
  • 对P进行左旋

1.2-3 要删除节点D为黑色,兄弟节点有右孩子,左孩子为空

操作如下:

  •  删除D节点
  • B的颜色变P的颜色
  • P的颜色变为黑色
  • 对P进行左旋

1.2-4 要删除节点为黑色,兄弟节点左右孩子都存在,且为红色

操作如下:

  •  删除D节点
  • 对P进行左旋
  • B的颜色变为P的颜色
  • P的颜色染为黑色
  • RN的颜色染为黑色

 1.2-5 要删除节点为黑色,兄弟节点为红色

对于这种情况的话,父节点P的颜色那就是必须为黑色了,操作如下:

  • 删除节点D
  • 对P进行左旋
  • B的颜色染黑
  • LN的颜色染红

这里只讨论了删除节点作为左叶子节点的情况,还有作为右叶子结点的情况还没有说,但是操作跟上面这5种是一模一样的,只是个对称而已,这里就不多说了,各位可以自己照着上面的方式进行画图理解

2.删除节点只有左孩子,没有右孩子

对于这种情况,也就只有下图这种样式:

  • 将D的值替换为LC的值
  • 删除LC节点 

3.删除节点只有右孩子,没有左孩子 

 对于这种情况,也是只有下图的样式:

  • 将D的值替换为RC的值
  • 删除RC节点

 4.删除节点有左右子节点,且都为红色

         对于这种情况处理,我们在前面学习二叉排序树的时候就已经知道了,首先要找到这个节点的后驱来替代这个节点,也就是在这个节点右子树找到最小的那个节点temp,替代这个被删除的节点D,然后问题就转换为删除temp节点,对于t删除emp节点就转化为上面三大类的删除情况(递归即可)。

 九、全部代码(C/C++)

#include
#include
#include
#include

//宏定义颜色
#define red 0
#define black 1

//数据类型Datatype
typedef char Datatype;
//红黑树节点存储结构
typedef struct node {
	Datatype data;
	int color;
	int key;
	struct node* par;//父节点指针
	struct node* left, * right;//左右子节点指针
}Node;

//红黑树的定义rbtree
typedef struct tree {
	Node* root;//指向根节点指针
	Node* nil;//叶子节点(哨兵)
}rbtree;


//创建初始化红黑树
rbtree* Create_inittree();
//插入数据
void Insert_node(rbtree* T, Datatype data, int key);
//已有数据,自增加key创建红黑树
void Create_wholetree(rbtree* T, Datatype* data, int n);
//中序遍历
void inorder_travel(Node* nil, Node* root);
//key查找
Node* Search_key(rbtree* T, int target);
//删除节点操作
void Delete_node(rbtree* T, Node* target);


				//主函数
int main() {
	rbtree* T = Create_inittree();
	Datatype d[] = { "ABCDEFG" };
	int n = sizeof(d) / sizeof(Datatype) - 1;
	Create_wholetree(T, d, n);

	inorder_travel(T->nil, T->root);

	Node* p = Search_key(T, 2);
	printf("查找结果 %d:%c\n", p->key, p->data);

	Delete_node(T, p);
	printf("删除后遍历结果:\n");
	inorder_travel(T->nil, T->root);
}

//创建初始化红黑树
rbtree* Create_inittree() {
	rbtree* T = (rbtree*)malloc(sizeof(rbtree));
	assert(T);
	T->nil = (Node*)malloc(sizeof(Node));
	assert(T->nil);
	//T->nil是不储存数据的节点,作为空节点代替NULL,也就是哨兵节点(表示空)
	T->nil->color = black;
	T->nil->par = NULL;
	T->nil->left = T->nil->right = NULL;
	T->root = T->nil;
	return T;
}

//创建一个节点
Node* Create_node(rbtree*T ,Datatype data, int key) {
	Node* new_node = (Node*)malloc(sizeof(Node));
	assert(new_node);
	new_node->data = data;
	new_node->color = red;//初始化颜色红色
	//左右父节点为nil哨兵节点
	new_node->left=new_node->right = T->nil;
	new_node->par = T->nil;
	new_node->key = key;
	return new_node;
}

//左旋(以x为旋转点,向左旋转)
void left_rotate(rbtree* T, Node* x) {
	Node* y = x->right;//标记到右子节点
	x->right = y->left;//y的左子节点代替x的右子节点
	if (x->right != T->nil)
		x->right->par = x;//如果不为空(nil)其父节点指向x
	y->par = x->par;//把y的父节点指向x的父节点,此时x与y没有直接联系了
	if (x->par == T->nil) {//判断x的父节点是否为根结点
		T->root = y;//如果是的话,y就变为根结点
	}
	else {
		//y顶替x的位置
		if (x == x->par->left)
			x->par->left = y;//如果x是父节点的左边,那y就代替x成为左子节点
		else
			x->par->right = y;//如果x是父节点的右边,那y就代替x成为右子节点
	}
	//y的左子节点指向x,x的父节点指向y
	y->left = x;
	x->par = y;
}
//右旋(以x为旋转点,向右旋转)
void right_rotate(rbtree* T, Node* x) {
	Node* y = x->left;//标记到左子节点y
	x->left = y->right;//y的右子节点代替x的左子节点
	if (x->left != T->nil)
		x->left->par = x;
	y->par = x->par;//y的父节点指向x的父节点
	if (x->par == T->nil)
		T->root = y;//如果x是根结点的话,那么y代替x成为根结点
	else {
		if (x == x->par->left)
			x->par->left = y;
		else
			x->par->right = y;
	}
	//y的右子节点指向x,x的父节点为y
	y->right = x;
	x->par = y;
}

//插入后平衡调整
void Insert_adjust(rbtree* T, Node* t) {
	//如果父节点的颜色是红色那就进行调整操作了
	if (t->par->color == red) {
		Node* p = t->par;
		Node* pp = p->par;
		//01 p节点是pp左子节点
		if (p == pp->left) {
			Node* uncle = pp->right;
			//01-1 叔叔节点颜色是红色
			if (uncle->color == red) {
				p->color = black;
				uncle->color = black;
				pp->color = red;
				t = pp;
			}
			//01-2 叔叔节点颜色是黑色
			else {
				//01-2-1 插入节点t是p的左子节点
				if (t == p->left) {
					p->color = black;
					pp->color = red;
					right_rotate(T, pp);
					t = p;
				}
				//01-2-2 插入节点t是p的右子节点
				else if(t==p->right){
					left_rotate(T, p);
					t->color = black;
					pp->color = red;
					right_rotate(T, pp);
				}
			}
		}
		//02 p节点是pp的右子节点
		else {		
			Node* uncle = pp->left;
			//02-1 叔叔节点颜色是红色
			if (uncle->color == red) {
				pp->color = red;
				p->color = black;
				uncle->color = black;
				t = pp;
			}
			//02-2 叔叔节点颜色是黑色
			else {
				//02-2-1 插入节点t是p的右子节点
				if (t == p->right) {
					p->color = black;
					pp->color = red;
					left_rotate(T,pp);
					t = p;
				}
				//02-2-2 插入节点t是p的左子节点
				else {
					right_rotate(T, p);
					t->color = black;
					pp->color = red;
					left_rotate(T, pp);
				}
			}
		}
	}
	//根节点标记黑色
	T->root->color = black;
}

//插入节点
void Insert_node(rbtree* T, Datatype data,int key) {
	assert(T);
	Node* t = Create_node(T ,data, key);
	Node* root = T->root;//快指针
	Node* cur=T->nil;//慢指针
	//1.如果根节点为空
	if (T->root==T->nil) {
		T->root = t;//根结点指向新创建的节点
	}
	else {
		while (root != T->nil) {
			cur = root;//cur标记为root的上一个节点(父节点)
			if (t->key > root->key)
				root = root->right;
			else if (t->key < root->key)
				root = root->left;
			//如果出现插入重复的key值,就退出,不进行插入操作
			else {
				printf("Don't insert the same key!\n");
				free(t);
				t = NULL;
				return;
			}
		}
	}
	//判断插入的位置
	if (key < cur->key)
		cur->left = t;//小的话就插入左边
	else
		cur->right = t;//大的话就插入右边
	t->par = cur;//新插入的父节点指针指向cur
	Insert_adjust(T, t);//平衡调整
}

//已有数据,自增加key创建红黑树
void Create_wholetree(rbtree* T, Datatype* data, int n) {
	for (int i = 0; i < n; i++) {
		Insert_node(T, data[i], i+1);
	}
}

//中序遍历
void inorder_travel(Node* nil,Node* root) {
	if (root == nil)
		return;
	inorder_travel(nil, root->left);
	printf("%d: %c\n", root->key, root->data);
	inorder_travel(nil, root->right);
}

//根据key查找
Node* Search_key(rbtree* T, int target) {
	assert(T);
	assert(T->root);
	Node* cur = T->root;
	while (cur) {
		if (cur->key == target)
			return cur;//找到就返回
		else if (cur->key > target)
			cur = cur->left;
		else
			cur = cur->right;
	}
	printf("The target is not exist\n");
	return NULL;
}

//删除黑色叶子节点调整
void Del_b_adjust(rbtree* T, Node* x) {
	//被删除节点x父节点的左边
	if (x == x->par->left) {
		Node* p = x->par;//父节点
		Node* b = p->right;//兄弟节点
		p->left = T->nil;
		//删除节点x
		free(x);
		x = NULL;
		//1.兄弟节点为黑色
		if (b->color == black) {
			//1-1没有侄子节点
			if (b->left == T->nil && b->right == T->nil) {
				p->color = black;
				b->color = red;
			}
			//1-2左侄节点红色
			else if (b->left->color == red && b->right == T->nil) {
				right_rotate(T, b);
				b->par->color = p->color;
				p->color = black;
				left_rotate(T, p);
			}
			//1-3右侄子节点红色
			else if (b->left == T->nil && b->right->color == red) {
				b->color = p->color;
				p->color = black;
				left_rotate(T, p);
			}
			//1-4 两个侄子节都是红色
			else {
				left_rotate(T, p);
				b->color = p->color;
				b->right->color = black;
				p->color = black;
			}
		}
		//2.兄弟节点为红色
		else {
			left_rotate(T, p);
			b->color = black;
			p->right->color = red;
		}
	}

	//被删除节点在父节点的右边
	else {
		Node* p = x->par;
		Node* b = p->left;
		p->right = T->nil;
		free(x);
		x = NULL;
		//1.兄弟节点黑色
		if (b->color == black) {
			//1-1没有侄子节点
			if (b->left == T->nil && b->right == T->nil) {
				p->color = black;
				b->color = red;
			}
			//1-2兄弟有右子节点
			else if (b->right->color == red && b->left == T->nil) {
				left_rotate(T, b);
				b->par->color = p->color;
				p->color = black;
				right_rotate(T, p);
			}
			//1-3 兄弟有左子节点
			else if (b->left->color == red && b->right == T->nil) {
				b->color = p->color;
				p->color = black;
				b->left->color = black;
				right_rotate(T, p);
			}
			//1-4 兄弟有左右子节点
			else {
				right_rotate(T, p);
				b->color = p->color;
				p->color = black;
				b->left->color = black;
			}
		}
		//2.兄弟节点为红色
		else {
			right_rotate(T, p);
			b->color = black;
			p->left->color = red;
		}
	}
}
//查找删除替身节点(找后驱)
Node* node_successor(rbtree* T, Node* root) {
	while (root->left != T->nil)
		root = root->left;
	return root;
}
//删除节点操作
void Delete_node(rbtree* T, Node* target) {
	//1.删除的节点是叶子节点
	if (target->left == T->nil && target->right == T->nil) {
		//1-01如果这个节点是红色节点
		if (target->color == red) {
			if (target == target->par->left)
				target->par->left = T->nil;
			else
				target->par->right = T->nil;
			free(target);
			target = NULL;
		}
		//1-02 如果是黑色叶子节点进入到调整
		else
			Del_b_adjust(T, target);
	}
	//2.删除的只有一个左孩子的节点
	else if (target->left != T->nil && target->right == T->nil) {
		Node* lc = target->left;
		target->data = lc->data;
		target->key = lc->key;
		target->left = T->nil;
		free(lc);
		lc = NULL;
	}
	//3.删除的只有一个右孩子的节点
	else if (target->left == T->nil && target->right != T->nil) {
		Node* rc = target->right;
		target->data = rc->data;
		target->key = rc->key;
		target->right = T->nil;
		free(rc);
		rc = NULL;
	}
	//4.删除的节点有左右孩子
	else {
		Node* sub = node_successor(T, target->right);//找到替代者
		target->data = sub->data;
		target->key = sub->key;
		Delete_node(T, sub);//递归进入到前三种删除方式
	}

	T->root->color = black;//根结点为黑色
}

 好了,以上就是红黑树的全部内容了,我们下一期开始学习新的数据结构----图,下次见咯!

分享一张壁纸: 

你可能感兴趣的:(数据结构与算法,数据结构,算法,c语言,红黑树,b树,二叉树,c++)