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

目录

前言

 一、什么是红黑树?

 二、为什么需要红黑树?(与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)其父节点指向xy->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成为左子节点elsex->par->right = y;//如果x是父节点的右边,那y就代替x成为右子节点}//y的左子节点指向x,x的父节点指向yy->left = x;x->par = y;
}

右旋(Right Rotation) 

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

操作如下:

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

 代码实现:

//右旋(以x为旋转点,向右旋转)
void right_rotate(rbtree* T, Node* x) {Node* y = x->left;//标记到左子节点yx->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;elsex->par->right = y;}//y的右子节点指向x,x的父节点为yy->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;elsecur = 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<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>//宏定义颜色
#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)其父节点指向xy->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成为左子节点elsex->par->right = y;//如果x是父节点的右边,那y就代替x成为右子节点}//y的左子节点指向x,x的父节点指向yy->left = x;x->par = y;
}
//右旋(以x为旋转点,向右旋转)
void right_rotate(rbtree* T, Node* x) {Node* y = x->left;//标记到左子节点yx->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;elsex->par->right = y;}//y的右子节点指向x,x的父节点为yy->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;//小的话就插入左边elsecur->right = t;//大的话就插入右边t->par = cur;//新插入的父节点指针指向curInsert_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;elsecur = 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;//删除节点xfree(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;elsetarget->par->right = T->nil;free(target);target = NULL;}//1-02 如果是黑色叶子节点进入到调整elseDel_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;//根结点为黑色
}

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

分享一张壁纸: 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/139683.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux-常用命令-基础命令-解压rar文件-unrar-x-命令-笔记】

【Linux-常用命令-基础命令-解压rar文件-unrar-x-命令-笔记】 1、前言2、操作3、自己的操作 1、前言 最近&#xff0c;在使用Linux的时&#xff0c;使用相关基础命令是&#xff0c;总是容易忘记&#xff0c;上网一搜&#xff0c;大部分都写的比较繁琐&#xff0c;解压不同文件…

单链表经典OJ题:反转链表

题目&#xff1a; 给你单链表的头节点 head &#xff0c;i请你反转链表&#xff0c;并返回反转后的链表。 图例&#xff1a; 分析&#xff1a; 根据链表的特征&#xff0c;反转链表的本质便是改变节点内部的指针方向。 将原先指向下一个节点的指针进行修改&#xff0c;将其的…

魔行观察》一款免费的品牌/商业地产数据查询平台

给大家推荐一款免费的商业数据查询平台"魔行观察"&#xff0c;可免费查询品牌&#xff0c;品牌门店&#xff0c;商场&#xff0c;全国小区&#xff0c;写字楼等相关信息&#xff0c;更多数据敬请期待 小程序搜索&#xff1a;魔行观察 即可使用

美创科技信创数据安全「利基者」!

近日&#xff0c;第一新声研究部正式发布《2023年中国信创网络安全产品竞争力象限》&#xff08;下称“象限报告“&#xff09;。 ◼︎ 象限报告综合考虑企业占有率、在技术/应用上的成熟度、在客户方面的交付完成度及口碑、产品在市场/营销/商业模式/行业拓展等战略上的领先性…

【学位论文】GB/T 7714-2015引用的快捷操作方法

GB/T 7714-2015《信息与文献参考文献著录规则》于2015年12月1日开始实施&#xff0c;成为了目前国内主流的学位论文引用格式之一。本文介绍一种比较方便简单的引用方法。 7714示例&#xff1a; [1] He K, Gkioxari G, Dollr P, et al. Mask r-cnn[C]//Proceedings of the IEEE …

TODO Vue typescript forEach的bug,需要再核實

forEach 一個string[]&#xff0c;只有最後一個匹配條件有效&#xff0c;其它條件無效。 所以&#xff0c;只能替換成普通的for循環。 console.log(taskList)// for (const _task of taskList.value) {// if (_task invoiceSendEmail) {// form.value.invoiceSendEmail…

Dify:三分钟搞定 小白也能定制自己的 AI 原生应用

01 前言 随着各个大模型厂商的逐步发力&#xff0c;市场上的优秀模型越来越多&#xff0c;基于这些大模型可以简易地创建可持续运营的原生 AI 应用。Dify应运而生&#xff01; 通过 Dify 创建的应用包含了&#xff1a; 开箱即用的的 Web 站点&#xff0c;支持表单模式和聊天…

Peter算法小课堂—蠕动区间

蠕动区间 蠕动区间&#xff08;尺取法、双游标&#xff09;是一个经典的优化算法。 我们以毛毛虫&#x1f41b;举例说明 具体的&#xff0c;我们看题目 例题 最小区间 这一题&#xff0c;我们用暴力法&#xff0c;复杂度O(N^2) 先给出暴力法代码 int ansn1; for(int tail…

行业追踪,2023-10-16

自动复盘 2023-10-16 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…

数据结构-----红黑树的插入

目录 前言 红黑树的储存结构 一、节点旋转操作 左旋&#xff08;Left Rotation&#xff09; 右旋&#xff08;Right Rotation&#xff09; 二、插入节点 1.插入的是空树 2.插入节点的key重新重复 3.插入节点的父节点是黑色 4.插入节点的父节点是红色 4.1父节点是祖父…

MIT6.S081Lab1: Xv6 and Unix utilities

MIT6.S081 Lab1&#xff1a; Xv6 and Unix utilities 官方文档 一.Boot xv6 如何成功的boot xv6可以看之前的文章MIT6.S081实验环境搭建&#xff0c;只是多一个步骤&#xff0c;在clone的文件夹中执行 git checkout util切换为util分支即可。 二.sleep 在user/sleep.c中编…

中国移动杭州公司——亚运会网络运行保障系统

一、项目背景 现代的大型赛事&#xff0c;不仅是场体育竞技的赛事&#xff0c;更是一场智能科技的竞赛。杭州亚运会通信保障指挥中心是今年5月正式启用的&#xff0c;具备态势感知、指挥调度、应急保障、客户响应的功能&#xff0c;依托AI智能、数字孪生、态势感知等技术&…