目录
前言
一、什么是红黑树?
二、为什么需要红黑树?(与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树有了高效率的查找功能,那需要红黑树干什么呢?下面看对比就知道了。
红黑树(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)
左旋是一种将某个节点的右子节点旋转上来的操作。也就是说当前节点的右子节点顶替了自己,然后自己变为右子节点的左子节点,以保持树的平衡。
操作如下:
- 将当前节点的右子节点设为新的父节点。
- 将新的父节点的左子节点设为当前节点的右子节点。
- 如果当前节点有父节点,将新的父节点替代当前节点的位置。
- 将当前节点设为新的父节点的左子节点。
代码实现:
//左旋(以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;
}
再讲之前,我分享一个网址给大家(链接:Red/Black Tree Visualization),这个是一个红黑树模拟器的网址,你们可以去进行红黑树插入删除遍历等操作,可以自己试试看。如下图所示:
废话不多说了,上正文!
红黑树的插入操作分两步走:
注意:插入节点初始为红色
原因分析:因为红黑树中任意一个节点到叶子节点路径所含黑色节点数量相同,也就是说如果我插入的节点为黑色的话,那么就会破坏红黑树的要求,所以插入的节点必须是红色节点,才能保证红黑树的性质。
下面就开始讨论红黑树的几种插入情况:
这是最简单的插入情况,当插入第一个节点的时候,红黑树为空我们只需要让根节点指向这个节点即可。操作如下:
- 根节点指向此节点
- 把根节点染黑
这种情况的话我们可以根据自己喜好去处理,如果出现了重复的key,那么就把这个key里面的值进行更新;或者我们不进行插入操作,因为key不可以重复,直接退出插入操作。
这很好处理,直接插入就行了,因为父节点为黑色,插入节点为红色,所以不会影响红黑树的平衡性。
- 直接插入即可
这种情况是最为复杂的,由于父节点颜色是红色,所以要进行平衡调整,所以要去进一步的讨论才行。那具体根据什么去调整呢?是看叔叔节点的颜色来调整(父节点的兄弟节点),具体分以下几种情况:
大的有两种情况,要看父节点是祖父节点的左边还是右边,下面我就以父节点为左子节点为例子:
下文图标说明:
t 表示插入的节点
P表示父节点
B表示叔叔节点
PP表示祖父节点
如果叔叔节点的颜色是红色的话,这里不需要进行旋转操作,只需要让父节点和叔叔节点颜色变为黑色,祖父节点颜色变为红色即可。流程如下:
这里的话又要去分两种情况:
- 插入节点是父节点的左子节点
- 插入节点是父节点的右子节点
如果插入的节点是父节点的左子节点的话,那么要进行以下操作:
如果插入节点是作为父节点的右子节点的话,要进行以下操作:
这里的操作跟4.1基本上是一模一样的,只是对称过去是了,但是我还是想详细列出来吧,下面接着看。
操作步骤如下:
同样的也是分以下两种情况讨论:
以上这些就是红黑树的插入全部可能了,是不是很多啊,其实还好啦!只要我们把这些情况一个一个分类,然后思路捋一捋很容易弄明白的,后面讲到红黑树的删除还有更多种情况呢!还有就是,这些图片是我自己画的,呃画得不太好,不好意思哈。
红黑树是二叉排序树,查找也跟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种情况)
- 兄弟节点没有左右孩子
- 兄弟节点左孩子为红色,右孩子为黑色
- 兄弟节点右孩子为红色,左孩子为黑色
- 兄弟节点有左右孩子,且都为红色
- 兄弟节点有左右孩子,且都为黑色(兄弟节点为红色)
- 删除的只有左子节点,没有右子节点
- 删除的只有右子节点,没有左子节点
- 删除的既有左子节点,又有右子节点
以上就是红黑树删除操作的全部情况,非常清晰,那这里就要去进行一个一个来讨论了。
以下图片标注说明
D:表示要删除的节点
P:表示删除节点的父节点
B:表示D的兄弟节点
LN:表示B的左子节点
RN:表示B的右子节点
如果删除的是叶子节点,那就要去看删除节点的颜色来操作,以下分两种情况:
注意事项
删除的是叶子结点,右两种可能,也就是要删除的叶子结点是左叶子结点或者是右叶子结点,下面我会去通过删除左叶子结点来去讨论上面这些过程,如果要删除右叶子结点,这里只需要进行对称操作就行了
直接删除,因为删除掉红色节点不会影响到红黑树的基本特性
如果要删除节点的颜色为黑色的话,那么这里就要考虑到被删除节点的兄弟节点的颜色了:
操作如下:
- 删除D节点
- P的颜色变为黑色
- B的颜色变为红色
操作如下:
- 删除D节点
- 对B进行右旋
- LN的颜色变为P的颜色
- P的颜色变为黑色
- 对P进行左旋
操作如下:
- 删除D节点
- B的颜色变P的颜色
- P的颜色变为黑色
- 对P进行左旋
操作如下:
- 删除D节点
- 对P进行左旋
- B的颜色变为P的颜色
- P的颜色染为黑色
- RN的颜色染为黑色
对于这种情况的话,父节点P的颜色那就是必须为黑色了,操作如下:
- 删除节点D
- 对P进行左旋
- B的颜色染黑
- LN的颜色染红
这里只讨论了删除节点作为左叶子节点的情况,还有作为右叶子结点的情况还没有说,但是操作跟上面这5种是一模一样的,只是个对称而已,这里就不多说了,各位可以自己照着上面的方式进行画图理解
对于这种情况,也就只有下图这种样式:
- 将D的值替换为LC的值
- 删除LC节点
对于这种情况,也是只有下图的样式:
- 将D的值替换为RC的值
- 删除RC节点
对于这种情况处理,我们在前面学习二叉排序树的时候就已经知道了,首先要找到这个节点的后驱来替代这个节点,也就是在这个节点右子树找到最小的那个节点temp,替代这个被删除的节点D,然后问题就转换为删除temp节点,对于t删除emp节点就转化为上面三大类的删除情况(递归即可)。
#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;//根结点为黑色
}
好了,以上就是红黑树的全部内容了,我们下一期开始学习新的数据结构----图,下次见咯!
分享一张壁纸: