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


    目录

    前言

     一、什么是红黑树?

     二、为什么需要红黑树?(与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 这条路径,发现有什么不同呢?很明显,这里不满足 “黑路同” 的性质,相较于其他路径这里多了一个黑色节点的数量。

    四、红黑树的储存结构

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

    1. //宏定义颜色
    2. #define red 0
    3. #define black 1
    4. //数据类型Datatype
    5. typedef char Datatype;
    6. //红黑树节点存储结构
    7. typedef struct node {
    8. Datatype data;
    9. int color;
    10. int key;//排序键值,根据key大小排序
    11. struct node* par;//父节点指针
    12. struct node* left, * right;//左右子节点指针
    13. }Node;
    14. //红黑树的定义rbtree
    15. typedef struct tree {
    16. Node* root;//指向根节点指针
    17. Node* nil;//叶子节点(哨兵)
    18. }rbtree;

     五、节点旋转操作

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

    左旋(Left Rotation)

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

    操作如下:

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

     代码实现:

    1. //左旋(以x为旋转点,向左旋转)
    2. void left_rotate(rbtree* T, Node* x) {
    3. Node* y = x->right;//标记到右子节点
    4. x->right = y->left;//y的左子节点代替x的右子节点
    5. if (x->right != T->nil)
    6. x->right->par = x;//如果不为空(nil)其父节点指向x
    7. y->par = x->par;//把y的父节点指向x的父节点,此时x与y没有直接联系了
    8. if (x->par == T->nil) {//判断x的父节点是否为根结点
    9. T->root = y;//如果是的话,y就变为根结点
    10. }
    11. else {
    12. //y顶替x的位置
    13. if (x == x->par->left)
    14. x->par->left = y;//如果x是父节点的左边,那y就代替x成为左子节点
    15. else
    16. x->par->right = y;//如果x是父节点的右边,那y就代替x成为右子节点
    17. }
    18. //y的左子节点指向x,x的父节点指向y
    19. y->left = x;
    20. x->par = y;
    21. }

    右旋(Right Rotation) 

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

    操作如下:

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

     代码实现:

    1. //右旋(以x为旋转点,向右旋转)
    2. void right_rotate(rbtree* T, Node* x) {
    3. Node* y = x->left;//标记到左子节点y
    4. x->left = y->right;//y的右子节点代替x的左子节点
    5. if (x->left != T->nil)
    6. x->left->par = x;
    7. y->par = x->par;//y的父节点指向x的父节点
    8. if (x->par == T->nil)
    9. T->root = y;//如果x是根结点的话,那么y代替x成为根结点
    10. else {
    11. if (x == x->par->left)
    12. x->par->left = y;
    13. else
    14. x->par->right = y;
    15. }
    16. //y的右子节点指向x,x的父节点为y
    17. y->right = x;
    18. x->par = y;
    19. }

    六、插入节点操作

    再讲之前,我分享一个网址给大家(链接: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的值的大小去向左向右查找,找到就返回即可。代码如下:

    1. //根据key查找
    2. Node* Search_key(rbtree* T, int target) {
    3. assert(T);
    4. assert(T->root);
    5. Node* cur = T->root;
    6. while (cur) {
    7. if (cur->key == target)
    8. return cur;//找到就返回
    9. else if (cur->key > target)
    10. cur = cur->left;
    11. else
    12. cur = cur->right;
    13. }
    14. printf("The target is not exist\n");
    15. return NULL;
    16. }

    八、红黑树的删除

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

    • 删除的是叶子节点(下面又分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++)

    1. #include
    2. #include
    3. #include
    4. #include
    5. //宏定义颜色
    6. #define red 0
    7. #define black 1
    8. //数据类型Datatype
    9. typedef char Datatype;
    10. //红黑树节点存储结构
    11. typedef struct node {
    12. Datatype data;
    13. int color;
    14. int key;
    15. struct node* par;//父节点指针
    16. struct node* left, * right;//左右子节点指针
    17. }Node;
    18. //红黑树的定义rbtree
    19. typedef struct tree {
    20. Node* root;//指向根节点指针
    21. Node* nil;//叶子节点(哨兵)
    22. }rbtree;
    23. //创建初始化红黑树
    24. rbtree* Create_inittree();
    25. //插入数据
    26. void Insert_node(rbtree* T, Datatype data, int key);
    27. //已有数据,自增加key创建红黑树
    28. void Create_wholetree(rbtree* T, Datatype* data, int n);
    29. //中序遍历
    30. void inorder_travel(Node* nil, Node* root);
    31. //key查找
    32. Node* Search_key(rbtree* T, int target);
    33. //删除节点操作
    34. void Delete_node(rbtree* T, Node* target);
    35. //主函数
    36. int main() {
    37. rbtree* T = Create_inittree();
    38. Datatype d[] = { "ABCDEFG" };
    39. int n = sizeof(d) / sizeof(Datatype) - 1;
    40. Create_wholetree(T, d, n);
    41. inorder_travel(T->nil, T->root);
    42. Node* p = Search_key(T, 2);
    43. printf("查找结果 %d:%c\n", p->key, p->data);
    44. Delete_node(T, p);
    45. printf("删除后遍历结果:\n");
    46. inorder_travel(T->nil, T->root);
    47. }
    48. //创建初始化红黑树
    49. rbtree* Create_inittree() {
    50. rbtree* T = (rbtree*)malloc(sizeof(rbtree));
    51. assert(T);
    52. T->nil = (Node*)malloc(sizeof(Node));
    53. assert(T->nil);
    54. //T->nil是不储存数据的节点,作为空节点代替NULL,也就是哨兵节点(表示空)
    55. T->nil->color = black;
    56. T->nil->par = NULL;
    57. T->nil->left = T->nil->right = NULL;
    58. T->root = T->nil;
    59. return T;
    60. }
    61. //创建一个节点
    62. Node* Create_node(rbtree*T ,Datatype data, int key) {
    63. Node* new_node = (Node*)malloc(sizeof(Node));
    64. assert(new_node);
    65. new_node->data = data;
    66. new_node->color = red;//初始化颜色红色
    67. //左右父节点为nil哨兵节点
    68. new_node->left=new_node->right = T->nil;
    69. new_node->par = T->nil;
    70. new_node->key = key;
    71. return new_node;
    72. }
    73. //左旋(以x为旋转点,向左旋转)
    74. void left_rotate(rbtree* T, Node* x) {
    75. Node* y = x->right;//标记到右子节点
    76. x->right = y->left;//y的左子节点代替x的右子节点
    77. if (x->right != T->nil)
    78. x->right->par = x;//如果不为空(nil)其父节点指向x
    79. y->par = x->par;//把y的父节点指向x的父节点,此时x与y没有直接联系了
    80. if (x->par == T->nil) {//判断x的父节点是否为根结点
    81. T->root = y;//如果是的话,y就变为根结点
    82. }
    83. else {
    84. //y顶替x的位置
    85. if (x == x->par->left)
    86. x->par->left = y;//如果x是父节点的左边,那y就代替x成为左子节点
    87. else
    88. x->par->right = y;//如果x是父节点的右边,那y就代替x成为右子节点
    89. }
    90. //y的左子节点指向x,x的父节点指向y
    91. y->left = x;
    92. x->par = y;
    93. }
    94. //右旋(以x为旋转点,向右旋转)
    95. void right_rotate(rbtree* T, Node* x) {
    96. Node* y = x->left;//标记到左子节点y
    97. x->left = y->right;//y的右子节点代替x的左子节点
    98. if (x->left != T->nil)
    99. x->left->par = x;
    100. y->par = x->par;//y的父节点指向x的父节点
    101. if (x->par == T->nil)
    102. T->root = y;//如果x是根结点的话,那么y代替x成为根结点
    103. else {
    104. if (x == x->par->left)
    105. x->par->left = y;
    106. else
    107. x->par->right = y;
    108. }
    109. //y的右子节点指向x,x的父节点为y
    110. y->right = x;
    111. x->par = y;
    112. }
    113. //插入后平衡调整
    114. void Insert_adjust(rbtree* T, Node* t) {
    115. //如果父节点的颜色是红色那就进行调整操作了
    116. if (t->par->color == red) {
    117. Node* p = t->par;
    118. Node* pp = p->par;
    119. //01 p节点是pp左子节点
    120. if (p == pp->left) {
    121. Node* uncle = pp->right;
    122. //01-1 叔叔节点颜色是红色
    123. if (uncle->color == red) {
    124. p->color = black;
    125. uncle->color = black;
    126. pp->color = red;
    127. t = pp;
    128. }
    129. //01-2 叔叔节点颜色是黑色
    130. else {
    131. //01-2-1 插入节点t是p的左子节点
    132. if (t == p->left) {
    133. p->color = black;
    134. pp->color = red;
    135. right_rotate(T, pp);
    136. t = p;
    137. }
    138. //01-2-2 插入节点t是p的右子节点
    139. else if(t==p->right){
    140. left_rotate(T, p);
    141. t->color = black;
    142. pp->color = red;
    143. right_rotate(T, pp);
    144. }
    145. }
    146. }
    147. //02 p节点是pp的右子节点
    148. else {
    149. Node* uncle = pp->left;
    150. //02-1 叔叔节点颜色是红色
    151. if (uncle->color == red) {
    152. pp->color = red;
    153. p->color = black;
    154. uncle->color = black;
    155. t = pp;
    156. }
    157. //02-2 叔叔节点颜色是黑色
    158. else {
    159. //02-2-1 插入节点t是p的右子节点
    160. if (t == p->right) {
    161. p->color = black;
    162. pp->color = red;
    163. left_rotate(T,pp);
    164. t = p;
    165. }
    166. //02-2-2 插入节点t是p的左子节点
    167. else {
    168. right_rotate(T, p);
    169. t->color = black;
    170. pp->color = red;
    171. left_rotate(T, pp);
    172. }
    173. }
    174. }
    175. }
    176. //根节点标记黑色
    177. T->root->color = black;
    178. }
    179. //插入节点
    180. void Insert_node(rbtree* T, Datatype data,int key) {
    181. assert(T);
    182. Node* t = Create_node(T ,data, key);
    183. Node* root = T->root;//快指针
    184. Node* cur=T->nil;//慢指针
    185. //1.如果根节点为空
    186. if (T->root==T->nil) {
    187. T->root = t;//根结点指向新创建的节点
    188. }
    189. else {
    190. while (root != T->nil) {
    191. cur = root;//cur标记为root的上一个节点(父节点)
    192. if (t->key > root->key)
    193. root = root->right;
    194. else if (t->key < root->key)
    195. root = root->left;
    196. //如果出现插入重复的key值,就退出,不进行插入操作
    197. else {
    198. printf("Don't insert the same key!\n");
    199. free(t);
    200. t = NULL;
    201. return;
    202. }
    203. }
    204. }
    205. //判断插入的位置
    206. if (key < cur->key)
    207. cur->left = t;//小的话就插入左边
    208. else
    209. cur->right = t;//大的话就插入右边
    210. t->par = cur;//新插入的父节点指针指向cur
    211. Insert_adjust(T, t);//平衡调整
    212. }
    213. //已有数据,自增加key创建红黑树
    214. void Create_wholetree(rbtree* T, Datatype* data, int n) {
    215. for (int i = 0; i < n; i++) {
    216. Insert_node(T, data[i], i+1);
    217. }
    218. }
    219. //中序遍历
    220. void inorder_travel(Node* nil,Node* root) {
    221. if (root == nil)
    222. return;
    223. inorder_travel(nil, root->left);
    224. printf("%d: %c\n", root->key, root->data);
    225. inorder_travel(nil, root->right);
    226. }
    227. //根据key查找
    228. Node* Search_key(rbtree* T, int target) {
    229. assert(T);
    230. assert(T->root);
    231. Node* cur = T->root;
    232. while (cur) {
    233. if (cur->key == target)
    234. return cur;//找到就返回
    235. else if (cur->key > target)
    236. cur = cur->left;
    237. else
    238. cur = cur->right;
    239. }
    240. printf("The target is not exist\n");
    241. return NULL;
    242. }
    243. //删除黑色叶子节点调整
    244. void Del_b_adjust(rbtree* T, Node* x) {
    245. //被删除节点x父节点的左边
    246. if (x == x->par->left) {
    247. Node* p = x->par;//父节点
    248. Node* b = p->right;//兄弟节点
    249. p->left = T->nil;
    250. //删除节点x
    251. free(x);
    252. x = NULL;
    253. //1.兄弟节点为黑色
    254. if (b->color == black) {
    255. //1-1没有侄子节点
    256. if (b->left == T->nil && b->right == T->nil) {
    257. p->color = black;
    258. b->color = red;
    259. }
    260. //1-2左侄节点红色
    261. else if (b->left->color == red && b->right == T->nil) {
    262. right_rotate(T, b);
    263. b->par->color = p->color;
    264. p->color = black;
    265. left_rotate(T, p);
    266. }
    267. //1-3右侄子节点红色
    268. else if (b->left == T->nil && b->right->color == red) {
    269. b->color = p->color;
    270. p->color = black;
    271. left_rotate(T, p);
    272. }
    273. //1-4 两个侄子节都是红色
    274. else {
    275. left_rotate(T, p);
    276. b->color = p->color;
    277. b->right->color = black;
    278. p->color = black;
    279. }
    280. }
    281. //2.兄弟节点为红色
    282. else {
    283. left_rotate(T, p);
    284. b->color = black;
    285. p->right->color = red;
    286. }
    287. }
    288. //被删除节点在父节点的右边
    289. else {
    290. Node* p = x->par;
    291. Node* b = p->left;
    292. p->right = T->nil;
    293. free(x);
    294. x = NULL;
    295. //1.兄弟节点黑色
    296. if (b->color == black) {
    297. //1-1没有侄子节点
    298. if (b->left == T->nil && b->right == T->nil) {
    299. p->color = black;
    300. b->color = red;
    301. }
    302. //1-2兄弟有右子节点
    303. else if (b->right->color == red && b->left == T->nil) {
    304. left_rotate(T, b);
    305. b->par->color = p->color;
    306. p->color = black;
    307. right_rotate(T, p);
    308. }
    309. //1-3 兄弟有左子节点
    310. else if (b->left->color == red && b->right == T->nil) {
    311. b->color = p->color;
    312. p->color = black;
    313. b->left->color = black;
    314. right_rotate(T, p);
    315. }
    316. //1-4 兄弟有左右子节点
    317. else {
    318. right_rotate(T, p);
    319. b->color = p->color;
    320. p->color = black;
    321. b->left->color = black;
    322. }
    323. }
    324. //2.兄弟节点为红色
    325. else {
    326. right_rotate(T, p);
    327. b->color = black;
    328. p->left->color = red;
    329. }
    330. }
    331. }
    332. //查找删除替身节点(找后驱)
    333. Node* node_successor(rbtree* T, Node* root) {
    334. while (root->left != T->nil)
    335. root = root->left;
    336. return root;
    337. }
    338. //删除节点操作
    339. void Delete_node(rbtree* T, Node* target) {
    340. //1.删除的节点是叶子节点
    341. if (target->left == T->nil && target->right == T->nil) {
    342. //1-01如果这个节点是红色节点
    343. if (target->color == red) {
    344. if (target == target->par->left)
    345. target->par->left = T->nil;
    346. else
    347. target->par->right = T->nil;
    348. free(target);
    349. target = NULL;
    350. }
    351. //1-02 如果是黑色叶子节点进入到调整
    352. else
    353. Del_b_adjust(T, target);
    354. }
    355. //2.删除的只有一个左孩子的节点
    356. else if (target->left != T->nil && target->right == T->nil) {
    357. Node* lc = target->left;
    358. target->data = lc->data;
    359. target->key = lc->key;
    360. target->left = T->nil;
    361. free(lc);
    362. lc = NULL;
    363. }
    364. //3.删除的只有一个右孩子的节点
    365. else if (target->left == T->nil && target->right != T->nil) {
    366. Node* rc = target->right;
    367. target->data = rc->data;
    368. target->key = rc->key;
    369. target->right = T->nil;
    370. free(rc);
    371. rc = NULL;
    372. }
    373. //4.删除的节点有左右孩子
    374. else {
    375. Node* sub = node_successor(T, target->right);//找到替代者
    376. target->data = sub->data;
    377. target->key = sub->key;
    378. Delete_node(T, sub);//递归进入到前三种删除方式
    379. }
    380. T->root->color = black;//根结点为黑色
    381. }

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

    分享一张壁纸: 

  • 相关阅读:
    PHP接收并处理请求中携带的xml格式的信息
    小项目第三天
    Qt开发 - Qt基础类型
    【ROS机器人】Autolabor OS最新版本已上线,免费下载
    长期戴耳机的危害有哪些?有哪些耳机不伤听力吗?
    赞叹AI的力量-TopazLabs 全家桶使用经历
    TRITC-透明质酸,TRITC-hyaluronic acid,四甲基罗丹明标记透明质酸
    JUNIT使用和注意、以及断言的介绍使用、SpringBoot Test测试类的使用、maven配置使用junit详细介绍
    Flutter学习:使用CustomPaint绘制路径
    用户画像系列——布隆过滤器在策略引擎中的应用
  • 原文地址:https://blog.csdn.net/m0_73633088/article/details/133886244