zoukankan      html  css  js  c++  java
  • 《算法导论》第十三章----红黑树

    《算法导论》学习记录目录

    红黑树是"平衡的"二叉查找树(PS: 关于二叉查找树的内容见链接)。普通的二叉查找树,在最坏情况下基本操作的运行时间为Θ(n),大部分的操作的最坏情况运行时间与树的高度成正比,对于相同的数据如何构造出一棵高效的二叉查找树(即高度尽量小)(如下图)

    图a为高效的二叉查找树,图b为低效的二叉查找树

    造成图b中的二叉查找树低效的原因在于建树的时候结点插入的顺序不恰当,选取关键字为2的结点(最小关键字结点)作为根结点使得所有的结点都位于根结点的右子树里。

    更糟糕是所有的结点都在同一条路上,这时候就相当于在链表上进行操作。

    但是普通的二叉查找树不会自动调整,使自己的尽量平衡(根结点的左右子树的高度接近相同),它只会根据插入结点的关键字来判断插入的位置。为了使其可以接近平衡,我们可以在结点里添加一些属性,然后根据这些属性来动态调整树的形状。(例如红黑树添加了颜色属性,AVL树添加了结点高度属性。)

    红黑树的基本性质:

    颜色属性:颜色为红色(RED)或者黑色(BLACK)。

    NIL结点:指向二叉查找数的外结点(叶子)指针,颜色为黑色;某些结点的父结点或者子结点指向NIL结点,相当于哨兵。(即为书中的nil[ T ])

    红黑性质:1、每个结点的颜色为红色或者黑色

         2、根结点的颜色为黑色

         3、每个叶结点都是黑色

            4、如果一个结点是红色的,那么它的两个儿子都是黑色的

             5、对于每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点

    黑高度:从该结点x出发(不包括该结点)到达叶结点的任意一条路径上,黑色结点的个数为该结点的黑色高度,用bh(x)

    具体如下图所示(深色为黑色结点,浅色为红色结点):

    下为红黑树结点结构体、红黑树结构体和NIL指针等基本实现

     1 #define RED 1
     2 #define BLACK 0
     3 
     4 typedef struct RB_TreeNode {
     5     int value;
     6     int color;
     7     struct RB_TreeNode * parent;
     8     struct RB_TreeNode * left;
     9     struct RB_TreeNode * right;
    10 }RB_TreeNode;                       //红黑树结点结构体
    11 
    12 typedef struct RB_Tree {
    13     RB_TreeNode * root;
    14 }RB_Tree;                           //红黑树结构体,根结点
    15 
    16 RB_TreeNode  NILL = { -1, BLACK, NULL, NULL, NULL };
    17 RB_TreeNode *NIL = &NILL;                               //NIL结点指针
    View Code

    一棵n个内结点的红黑树的高度最多为2lg(n+1),这是红黑树是一种好的查找树的原因。(PS:证明见书本,本人太菜,看完不会用自己的话总结。。。所以就不证明)

    因此查找、插入、找最小值、找出最大值、找出前趋、找出后继、删除动态集合操作在红黑树上可以在O(lgn)时间内完成。

    结点的旋转

    因为直接会之前二叉查找树的插入、删除的实现不能保证操作后的二叉树还是红黑树,所以我们添加旋转操作,同过旋转操作来使红黑树在插入、删除操作后还能保持本身的性质。

    旋转分为左旋转、右旋转(如下图所示)

    通过旋转,我们可以修改结点的指针域使结点满足自己的需求。

    下为左旋转、右旋转的实现

     1 /*
     2  * x结点上进行左旋转,y结点(x结点的右儿子)的左儿子成为x结点的新右儿子
     3  * x结点成为y结点的左儿子的新父结点
     4  * x结点的父结点成为y结点的新父结点,y结点成为x结点的新父结点
     5  * x结点成为y结点的新左儿子
     6  */
     7 void Left_Rotate(RB_Tree *T, RB_TreeNode *x){
     8     RB_TreeNode *y = x->right;       //x点右儿子          
     9     x->right = y->left;              //y结点的左儿子会成为x结点的右儿子
    10     if(y->left != NIL)
    11         y->left->parent = x;         //如果y有左儿子,y的左儿子的父结点为x
    12     y->parent = x->parent;
    13     if(x->parent == NIL)
    14         T->root = y;                //如果x的父结点为哨兵,说明x为根结点,则y成为根结点
    15     else if(x == x->parent->left)
    16         x->parent->left = y;        
    17     else
    18         x->parent->right = y;       //判断x为其父结点的左、右儿子,y成为x父结点对应的儿子
    19     y->left = x;                    //y的左儿子为x
    20     x->parent = y;                  //x的父结点为y
    21 }
    22 
    23 /*
    24  * x结点上进行右旋转,y结点(x结点的左儿子)的右儿子成为x结点的新左儿子
    25  * x结点成为y结点的右儿子的新父结点
    26  * x结点的父结点成为y结点的新父结点,y结点成为x结点的新父结点
    27  * x结点成为y结点的新右儿子
    28  * PS:代码的解释可参照左旋转
    29  */
    30 void Right_Rotate(RB_Tree *T, RB_TreeNode *x){
    31     RB_TreeNode *y = x->left;
    32     x->left = y->right;
    33     if(y->right != NIL)
    34         y->right->parent = x;
    35     y->parent = x->parent;
    36     if(x->parent == NIL)
    37         T->root = y;
    38     else if(x == x->parent->left)
    39         x->parent->left = y;
    40     else
    41         x->parent->right = y;
    42     y->right = x;
    43     x->parent = y;
    44 }
    View Code

    选择效果例子如下图:

    有了旋转操作,我们就可以进行插入和删除操作。红黑树的插入和删除,是基于普通二叉查找树的插入和删除,再添加维持红黑性质的操作(PS:二叉查找树的插入、删除操作见之前的博客)。

    插入

    当新的结点插入到红黑树时,我们将该结点的颜色设置为红色,这样红黑树的性质1(没有结点存在其他颜色)、3(新结点的左右儿子为NIL,黑色结点)、5(添加红色结点不影响黑色结点的数目)没改变,但是有性质2(新结点有可能为根结点)和性质4(新结点的父结点有可能也是红色的)。PS:该段中括号为对应的解释。。。。

    插入新结点后,如果它不是根结点而且它的父结点不是红色的,那么就无需做有关调整。但是如果不符合红黑性质的时候就需要做有关调整,如果其为根结点,直接将其颜色设置为黑色即可。除此之外,还存在3种情况不符合红黑性质(PS:当前的情况可能经过一次调整后成为其他情况。具体来讲应该有6种情况,不过有3种和另外3种对称,下文是考虑z结点的父结点为左儿子):

    情况1:结点z的叔叔是红色。这样我们直接将z父亲的父亲颜色变为红色,z的父亲和叔叔的颜色变为黑色(这样性质5没有改变),最后将z父亲的父亲作为新的z结点,继续上移,直到z结点的父结点为黑色或者z为根结点(当z为根结点,直接将其颜色改为黑色,这样整个红黑树就比原来多了一个黑色结点。但是改变颜色前已经经过各种调整,只违反性质2,改变颜色后,就没有违反任何红黑性质)。如下图所示:

    情况2:结点z的叔叔是黑色的、而且z是右儿子(情况2可以通过旋转成为情况3)

    情况3:结点z的叔叔是黑色的、而且z是左儿子(PS:如果z结点的父结点为右儿子的时候情况2要改为“z是左儿子”,情况3要改为“z是右儿子”)因为z的叔叔是黑色,所以不可以直接通过改变颜色来维持红黑性质,要通过旋转操作来维持。如下图所示,如果为情况2就通过对A进行左旋转变为情况3,再改变结点的颜色,最后将C进行右旋转。这样就使得解决违反性质4,而且不违反性质5.(PS:如果z结点的父结点为右儿子的时候,应将左旋转改为右旋转,右旋转改为左旋转。具体画图理一理就明白

    下为插入和插入调整实现(PS:插入的注释见二叉查找树的插入)

     1 /*
     2  * 插入函数,注意插入结点与其在树中对应的父结点的链接(需要记录父结点)。
     3  * 从根结点出发,不停用当前结点与插入的值比较,如果当前结点的值比较大就往当前结点的左儿子走,相反就往右儿子走,直到当前结点为空,
     4  * 在过程中记录当前结点的父结点。
     5  * 运行时间为O(h),h为树的高度。因为整个过程是一条沿着根结点下降的路径。
     6  */
     7 void RB_Insert(RB_Tree *T, int key){
     8     RB_TreeNode *z;
     9     z = (RB_TreeNode *)malloc(sizeof(RB_TreeNode));         //新建结点
    10     z->value = key;
    11     z->color = RED;
    12     z->parent = z->left = z->right = NIL;
    13     RB_TreeNode *x = T->root;
    14     RB_TreeNode *y = NIL;
    15     while(x != NIL){
    16         y = x;
    17         if(x->value < key)
    18             x = x->right;
    19         else
    20             x = x->left;
    21     }
    22     z->parent = y;
    23     if(y == NIL){
    24         T->root = z;
    25         T->root->parent = NIL;
    26         T->root->parent->color = BLACK;
    27     }
    28     else if(z->value < y->value)
    29         y->left = z;
    30     else
    31         y->right = z;
    32     RB_Insert_Fixup(T, z);                                 //插入调整,维持红黑性质
    33 }
    34 
    35 /*
    36  * 插入调整,维持红黑性质
    37  */
    38 void RB_Insert_Fixup(RB_Tree *T, RB_TreeNode *z){
    39     while(z->parent->color == RED){                         //如果z结点的父结点为黑色,就不用进入3种情况的处理
    40         if(z->parent == z->parent->parent ->left){          //z的父结点为左儿子的情况
    41             RB_TreeNode *y = z->parent->parent->right;
    42             if(y->color == RED){                            //情况1
    43                 z->parent->color = BLACK;
    44                 y->color = BLACK;
    45                 z->parent->parent->color = RED;
    46                 z = z->parent->parent;
    47             }                                           
    48             else {
    49                 if(z == z->parent->right){                  //情况2
    50                     z = z->parent;
    51                     Left_Rotate(T, z);                      //左旋转,情况2变为3
    52                 }
    53                 z->parent->color = BLACK;                   //情况3
    54                 z->parent->parent->color = RED;
    55                 Right_Rotate(T, z->parent->parent);         //右选择解决违反性质4
    56             }
    57         }
    58         else{
    59             RB_TreeNode *y = z->parent->parent->left;       //z的结点为右儿子的情况
    60             if(y->color == RED){                            //情况1
    61                 z->parent->color = BLACK;
    62                 y->color = BLACK;
    63                 z->parent->parent->color = RED;
    64                 z = z->parent->parent;
    65             }
    66             else{
    67                 if(z == z->parent->left){                   //情况2
    68                     z = z->parent;
    69                     Right_Rotate(T, z);                     //右旋转,情况2变为情况3
    70                 }
    71                 z->parent->color = BLACK;                   //情况3
    72                 z->parent->parent->color = RED;
    73                 Left_Rotate(T, z->parent->parent);          //左旋转解决违反性质4
    74             }
    75         }
    76     }
    77     T->root->color = BLACK;                                 //如果红黑树T的根结点为红色就会变成黑色,如果是黑色变成黑色也没影响
    78 }
    View Code

    红黑树的插入操作总共花费时间为O(lgn)。因为插入部分花费O(lgn),而插入调整只有情况1执行的时候才可能要重复进行while循环,因此while循环可能的运行次数为O(lgn)。所以总共花费为O(lgn)。

    删除(PS:下文的z为想删除的结点、y结点为实际要删除的结点、x结点为y的唯一孩子结点。如果觉得想删除结点、实际删除结点、实际删除结点的唯一孩子感到困惑,请看二叉查找树,里面有解释,虽然里面的命名又点不一样。。。)

    对于删除结点,如果删除的是红色的结点,对红黑性质没有任何影响。但是如果删除的是黑色结点,就会产生3个问题:1、如果y是根结点,而y的一个红色孩子成为了根结点,违反了性质2;2、如果x和y的父结点都是红色,违反性质4;3、删除y导致包含y的任何路径黑结点个数少1,违反了性质5,解决这个问题的办法是x添加多一层黑色(双重黑色或红黑),这样就由违反性质5变为违反性质1。(PS:如果要恢复性质5会很麻烦。。。。这个我是看网友博客了。。还没探究这个问题)

    对于删除调整如何恢复性质2、4:删除调整实现里对于x为根结点或者x的颜色为红色时直接将x结点的颜色变成黑色。如果x为根结点,直接改变为黑色即可,如果x不为根结点,将x变成黑色,可以解决违反性质4,而且之前包含y的任何路径的黑色结点个数也恢复。(这个是我自己看完伪代码想的,书中要求读者说明删除调整代码如何恢复2、4,所以可能有错,欢迎指出错误)。

    删除调整主要将额外的黑色沿树上移,直到:1、x指向一个红黑结点,直接将x的颜色变为黑色

                        2、x指向根,直接消除额外的黑色

                        3、或者做必要的旋转和颜色修改

    总共8情况(4种与另外4种对称,下文是考虑x为左儿子的情况)(下文的x结点为需要调整的结点,即增加了额外一层黑色)

    情况1:x的兄弟w是红色。先改变w和x的父结点的颜色,对x的父结点进行一次做旋转,红黑性质继续维持,但是我们通过这些操作使得情况1转换成情况2、3、4

    情况2:x的兄弟w是黑色,而且w的两个孩子都是黑色。x和w的去掉一重黑色,那么x只有一重黑色,而w变为红色(因为w的两个儿子都是黑色所以变成红色没有违反性质4)。然后我们再在x的父结点上添加一层额外的黑色,最后x的父结点成为新的x结点继续我和循环。

    情况3:x的兄弟w是黑色的,w的左孩子是红色,右孩子是黑色的。首先交换w和其左孩子的颜色,对w进行右旋转。这样就从情况3转换成情况4.

    情况4:x的兄弟w是黑色的,而且w的右孩子是红色的。首先交换w和x的父结点的颜色,对x的父结点进行左旋转,去掉x的额外黑色,又不破坏红黑性质。最后将x置为根结点,接受循环。

    (PS:x为右儿子的情况时,需要将上文的左旋转换成右旋转,右旋转换成左旋转)

    具体如下图所示

    下为删除和删除调整实现:

     1 /*
     2  * 删除结点函数,首先要确定真正删除的结点是那个。
     3  * 如果z没有子结点,直接将z的父结点对应的指针指向NULL
     4  * 如果z只有一个子节点,直接将z的父结点对应的指针指向z的子结点
     5  * 如果z有两个子结点,实际上要删除的不是z,而是z的后继,,再用z的后继的内容代替z的内容
     6  */
     7 
     8 void RB_Delete(RB_Tree *T, RB_TreeNode *z){
     9     RB_TreeNode *x = NULL;
    10     RB_TreeNode *y = NULL;
    11 
    12     if(z->left == NIL || z->right == NIL)
    13         y = z;
    14     else
    15         y = RB_Tree_Successor(z);
    16     if(y->left != NIL)
    17         x = y->left;
    18     else
    19         x = y->right;
    20     x->parent = y->parent;
    21     if(y->parent == NIL)
    22         T->root = x;
    23     else if(y == y->parent->left)
    24         y->parent->left = x;
    25     else
    26         y->parent->right = x;
    27     if(y != z)
    28         z->value = y->value;
    29     if(y->color == BLACK)
    30         RB_Delete_Fixup(T, x);          //当实际要删除的结点y为黑色时才需要进行调整
    31 }
    32 
    33 /*
    34  * 删除调整,使红黑树维持红黑性质
    35  */
    36 void RB_Delete_Fixup(RB_Tree *T, RB_TreeNode *x){
    37     while(x != T->root && x->color == BLACK){               //当x结点为根结点或者x的颜色为红色(即为红黑, 实际上额外黑色没有直接加上去,只是默认x节点有一重额外的黑色)
    38         if(x == x->parent->left){                           //x为左儿子情况
    39             RB_TreeNode *w = x->parent->right;
    40             if(w->color == RED){                            //情况1
    41                 w->color = BLACK;
    42                 x->parent->color = RED;
    43                 Left_Rotate(T, x->parent);                  //左旋转,使得情况1转换成情况2、3或4
    44                 w = x->parent->right;                       
    45             }
    46             
    47             if(w->left->color == BLACK && w->right->color == BLACK){      //情况2
    48                  w->color = RED;                            //修改颜色
    49                  x = x->parent;                             //上移
    50             }
    51             else{
    52                 if(w->right->color == BLACK){               //情况3
    53                     w->left->color = BLACK;
    54                     w->color = RED;
    55                     Right_Rotate(T, w);                     //右旋转,情况3转换成情况4
    56                     w = x->parent->right;
    57                 }
    58                 w->color = x->parent->color;
    59                 x->parent->color = BLACK;
    60                 w->right->color = BLACK;
    61                 Left_Rotate(T, x->parent);                  //左旋转,去掉x的额外黑色
    62                 x = T->root;                                //使循环结束
    63             }
    64             
    65         }
    66         else{                                               //x为右儿子情况(PS:下文的注释参考上文)
    67             RB_TreeNode *w = x->parent->left;
    68             if(w->color == RED){
    69                 w->color = BLACK;
    70                 x->parent->color = RED;
    71                 Right_Rotate(T, x->parent);
    72                 w = x->parent->left;
    73             }
    74             
    75             if(w->left->color == BLACK && w->right->color == BLACK){
    76                 w->color = RED;
    77                 x = x->parent;
    78             }
    79             else {
    80                 if(w->left->color == BLACK){
    81                     w->right->color = BLACK;
    82                     w->color = RED;
    83                     Left_Rotate(T, w);
    84                     w = x->parent->left;
    85                 }
    86                 w->color = x->parent->color;
    87                 x->parent->color = BLACK;
    88                 w->left->color = BLACK;
    89                 Right_Rotate(T, x->parent);
    90                 x = T->root;
    91             }
    92         }
    93     }
    94 }
    View Code

    删除操作的总运行时间为O(lgn)。

    下为全部代码(除了删除、插入实现的注释见二叉查找树

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 
      4 #define RED 1
      5 #define BLACK 0
      6 
      7 typedef struct RB_TreeNode {
      8     int value;
      9     int color;
     10     struct RB_TreeNode * parent;
     11     struct RB_TreeNode * left;
     12     struct RB_TreeNode * right;
     13 }RB_TreeNode;                       //红黑树结点结构体
     14 
     15 typedef struct RB_Tree {
     16     RB_TreeNode * root;
     17 }RB_Tree;                           //红黑树结构体,根结点
     18 
     19 RB_TreeNode  NILL = { -1, BLACK, NULL, NULL, NULL };
     20 RB_TreeNode *NIL = &NILL;                               //NIL结点指针
     21 
     22 void Left_Rotate(RB_Tree *T, RB_TreeNode *x);
     23 
     24 void Right_Rotate(RB_Tree *T, RB_TreeNode *x);
     25 
     26 void RB_Insert(RB_Tree *T, int key);
     27 
     28 void RB_Insert_Fixup(RB_Tree *T, RB_TreeNode *z);
     29 
     30 void RB_Delete(RB_Tree *T, RB_TreeNode *z);
     31 
     32 void RB_Delete_Fixup(RB_Tree *T, RB_TreeNode *x);
     33 
     34 RB_TreeNode * RB_Search(RB_TreeNode *x, int key);
     35 
     36 RB_TreeNode * RB_Tree_Minimum(RB_TreeNode *x);
     37 
     38 RB_TreeNode * RB_Tree_Maximum(RB_TreeNode *x);
     39 
     40 RB_TreeNode * RB_Tree_Successor(RB_TreeNode *x);
     41 
     42 RB_TreeNode * RB_Tree_Predecesor(RB_TreeNode *x);
     43 
     44 void Inorder_RB_Tree_Walk(RB_TreeNode *x);
     45 
     46 void free_men(RB_TreeNode *x);
     47 
     48 int main(){
     49     RB_Tree *T;
     50     T->root = NIL;
     51 
     52     int n, value, i;
     53     scanf("%d", &n);
     54     for(i = 1; i<= n; i++){
     55         scanf("%d", &value);
     56         RB_Insert(T, value);
     57         Inorder_RB_Tree_Walk(T->root);    
     58         printf("
    ");
     59     }
     60     RB_TreeNode *s = RB_Search(T->root, 3);
     61     if(s != NIL)
     62         printf("%d is exists!
    ", s->value);
     63     /*Inorder_RB_Tree_Walk(T->root);
     64 
     65     printf("
    ");*/
     66     printf("%d
    ", RB_Tree_Minimum(T->root)->value);
     67     printf("%d
    ", RB_Tree_Maximum(T->root)->value);
     68     printf("%d
    ", RB_Tree_Successor(s)->value);
     69     printf("%d
    ", RB_Tree_Predecesor(s)->value);
     70     RB_Delete(T, s);
     71     Inorder_RB_Tree_Walk(T->root);
     72     printf("
    ");
     73     return 0;
     74 }
     75 
     76 /*
     77  * x结点上进行左旋转,y结点(x结点的右儿子)的左儿子成为x结点的新右儿子
     78  * x结点成为y结点的左儿子的新父结点
     79  * x结点的父结点成为y结点的新父结点,y结点成为x结点的新父结点
     80  * x结点成为y结点的新左儿子
     81  */
     82 void Left_Rotate(RB_Tree *T, RB_TreeNode *x){
     83     RB_TreeNode *y = x->right;       //x点右儿子          
     84     x->right = y->left;              //y结点的左儿子会成为x结点的右儿子
     85     if(y->left != NIL)
     86         y->left->parent = x;         //如果y有左儿子,y的左儿子的父结点为x
     87     y->parent = x->parent;
     88     if(x->parent == NIL)
     89         T->root = y;                //如果x的父结点为哨兵,说明x为根结点,则y成为根结点
     90     else if(x == x->parent->left)
     91         x->parent->left = y;        
     92     else
     93         x->parent->right = y;       //判断x为其父结点的左、右儿子,y成为x父结点对应的儿子
     94     y->left = x;                    //y的左儿子为x
     95     x->parent = y;                  //x的父结点为y
     96 }
     97 
     98 /*
     99  * x结点上进行右旋转,y结点(x结点的左儿子)的右儿子成为x结点的新左儿子
    100  * x结点成为y结点的右儿子的新父结点
    101  * x结点的父结点成为y结点的新父结点,y结点成为x结点的新父结点
    102  * x结点成为y结点的新右儿子
    103  * PS:代码的解释可参照左旋转
    104  */
    105 void Right_Rotate(RB_Tree *T, RB_TreeNode *x){
    106     RB_TreeNode *y = x->left;
    107     x->left = y->right;
    108     if(y->right != NIL)
    109         y->right->parent = x;
    110     y->parent = x->parent;
    111     if(x->parent == NIL)
    112         T->root = y;
    113     else if(x == x->parent->left)
    114         x->parent->left = y;
    115     else
    116         x->parent->right = y;
    117     y->right = x;
    118     x->parent = y;
    119 }
    120 
    121 /*
    122  * 插入函数,注意插入结点与其在树中对应的父结点的链接(需要记录父结点)。
    123  * 从根结点出发,不停用当前结点与插入的值比较,如果当前结点的值比较大就往当前结点的左儿子走,相反就往右儿子走,直到当前结点为空,
    124  * 在过程中记录当前结点的父结点。
    125  * 运行时间为O(h),h为树的高度。因为整个过程是一条沿着根结点下降的路径。
    126  */
    127 void RB_Insert(RB_Tree *T, int key){
    128     RB_TreeNode *z;
    129     z = (RB_TreeNode *)malloc(sizeof(RB_TreeNode));         //新建结点
    130     z->value = key;
    131     z->color = RED;
    132     z->parent = z->left = z->right = NIL;
    133     RB_TreeNode *x = T->root;
    134     RB_TreeNode *y = NIL;
    135     while(x != NIL){
    136         y = x;
    137         if(x->value < key)
    138             x = x->right;
    139         else
    140             x = x->left;
    141     }
    142     z->parent = y;
    143     if(y == NIL){
    144         T->root = z;
    145         T->root->parent = NIL;
    146         T->root->parent->color = BLACK;
    147     }
    148     else if(z->value < y->value)
    149         y->left = z;
    150     else
    151         y->right = z;
    152     RB_Insert_Fixup(T, z);                                 //插入调整,维持红黑性质
    153 }
    154 
    155 /*
    156  * 插入调整,维持红黑性质
    157  */
    158 void RB_Insert_Fixup(RB_Tree *T, RB_TreeNode *z){
    159     while(z->parent->color == RED){                         //如果z结点的父结点为黑色,就不用进入3种情况的处理
    160         if(z->parent == z->parent->parent ->left){          //z的父结点为左儿子的情况
    161             RB_TreeNode *y = z->parent->parent->right;
    162             if(y->color == RED){                            //情况1
    163                 z->parent->color = BLACK;
    164                 y->color = BLACK;
    165                 z->parent->parent->color = RED;
    166                 z = z->parent->parent;
    167             }                                           
    168             else {
    169                 if(z == z->parent->right){                  //情况2
    170                     z = z->parent;
    171                     Left_Rotate(T, z);                      //左旋转,情况2变为3
    172                 }
    173                 z->parent->color = BLACK;                   //情况3
    174                 z->parent->parent->color = RED;
    175                 Right_Rotate(T, z->parent->parent);         //右选择解决违反性质4
    176             }
    177         }
    178         else{
    179             RB_TreeNode *y = z->parent->parent->left;       //z的结点为右儿子的情况
    180             if(y->color == RED){                            //情况1
    181                 z->parent->color = BLACK;
    182                 y->color = BLACK;
    183                 z->parent->parent->color = RED;
    184                 z = z->parent->parent;
    185             }
    186             else{
    187                 if(z == z->parent->left){                   //情况2
    188                     z = z->parent;
    189                     Right_Rotate(T, z);                     //右旋转,情况2变为情况3
    190                 }
    191                 z->parent->color = BLACK;                   //情况3
    192                 z->parent->parent->color = RED;
    193                 Left_Rotate(T, z->parent->parent);          //左旋转解决违反性质4
    194             }
    195         }
    196     }
    197     T->root->color = BLACK;                                 //如果红黑树T的根结点为红色就会变成黑色,如果是黑色变成黑色也没影响
    198 }
    199 
    200 /*
    201  * 删除结点函数,首先要确定真正删除的结点是那个。
    202  * 如果z没有子结点,直接将z的父结点对应的指针指向NULL
    203  * 如果z只有一个子节点,直接将z的父结点对应的指针指向z的子结点
    204  * 如果z有两个子结点,实际上要删除的不是z,而是z的后继,,再用z的后继的内容代替z的内容
    205  */
    206 
    207 void RB_Delete(RB_Tree *T, RB_TreeNode *z){
    208     RB_TreeNode *x = NULL;
    209     RB_TreeNode *y = NULL;
    210 
    211     if(z->left == NIL || z->right == NIL)
    212         y = z;
    213     else
    214         y = RB_Tree_Successor(z);
    215     if(y->left != NIL)
    216         x = y->left;
    217     else
    218         x = y->right;
    219     x->parent = y->parent;
    220     if(y->parent == NIL)
    221         T->root = x;
    222     else if(y == y->parent->left)
    223         y->parent->left = x;
    224     else
    225         y->parent->right = x;
    226     if(y != z)
    227         z->value = y->value;
    228     if(y->color == BLACK)
    229         RB_Delete_Fixup(T, x);          //当实际要删除的结点y为黑色时才需要进行调整
    230 }
    231 
    232 /*
    233  * 删除调整,使红黑树维持红黑性质
    234  */
    235 void RB_Delete_Fixup(RB_Tree *T, RB_TreeNode *x){
    236     while(x != T->root && x->color == BLACK){               //当x结点为根结点或者x的颜色为红色(即为红黑, 实际上额外黑色没有直接加上去,只是默认x节点有一重额外的黑色)
    237         if(x == x->parent->left){                           //x为左儿子情况
    238             RB_TreeNode *w = x->parent->right;
    239             if(w->color == RED){                            //情况1
    240                 w->color = BLACK;
    241                 x->parent->color = RED;
    242                 Left_Rotate(T, x->parent);                  //左旋转,使得情况1转换成情况2、3或4
    243                 w = x->parent->right;                       
    244             }
    245             
    246             if(w->left->color == BLACK && w->right->color == BLACK){      //情况2
    247                  w->color = RED;                            //修改颜色
    248                  x = x->parent;                             //上移
    249             }
    250             else{
    251                 if(w->right->color == BLACK){               //情况3
    252                     w->left->color = BLACK;
    253                     w->color = RED;
    254                     Right_Rotate(T, w);                     //右旋转,情况3转换成情况4
    255                     w = x->parent->right;
    256                 }
    257                 w->color = x->parent->color;
    258                 x->parent->color = BLACK;
    259                 w->right->color = BLACK;
    260                 Left_Rotate(T, x->parent);                  //左旋转,去掉x的额外黑色
    261                 x = T->root;                                //使循环结束
    262             }
    263             
    264         }
    265         else{                                               //x为右儿子情况(PS:下文的注释参考上文)
    266             RB_TreeNode *w = x->parent->left;
    267             if(w->color == RED){
    268                 w->color = BLACK;
    269                 x->parent->color = RED;
    270                 Right_Rotate(T, x->parent);
    271                 w = x->parent->left;
    272             }
    273             
    274             if(w->left->color == BLACK && w->right->color == BLACK){
    275                 w->color = RED;
    276                 x = x->parent;
    277             }
    278             else {
    279                 if(w->left->color == BLACK){
    280                     w->right->color = BLACK;
    281                     w->color = RED;
    282                     Left_Rotate(T, w);
    283                     w = x->parent->left;
    284                 }
    285                 w->color = x->parent->color;
    286                 x->parent->color = BLACK;
    287                 w->left->color = BLACK;
    288                 Right_Rotate(T, x->parent);
    289                 x = T->root;
    290             }
    291         }
    292     }
    293 }
    294 
    295 
    296 RB_TreeNode * RB_Search(RB_TreeNode *x, int key){
    297     if(x->value == key || x == NIL)
    298         return x;
    299     if(x->value > key)
    300         RB_Search(x->left, key);
    301     else
    302         RB_Search(x->right, key);
    303 }
    304 
    305 RB_TreeNode * RB_Tree_Minimum(RB_TreeNode *x){
    306     RB_TreeNode *r = x;
    307     while(r->left != NIL)
    308         r = r->left;
    309     return r;
    310 }
    311 
    312 RB_TreeNode * RB_Tree_Maximum(RB_TreeNode *x){
    313     RB_TreeNode *r = x;
    314     while(r->left != NIL)
    315         r = r->left;
    316     return r;
    317 }
    318 
    319 RB_TreeNode * RB_Tree_Successor(RB_TreeNode *x){
    320     RB_TreeNode *r = x;
    321     if(r->right != NIL)
    322         return RB_Tree_Minimum(r->right);
    323     RB_TreeNode *y = r->parent;
    324     while(y != NIL && r == y->right){
    325         r = y;
    326         y = y->parent;
    327     }
    328     return y;
    329 }
    330 RB_TreeNode * RB_Tree_Predecesor(RB_TreeNode *x){
    331     RB_TreeNode *r = x;
    332     if(r->left != NIL)
    333         return RB_Tree_Maximum(r->left);
    334     RB_TreeNode *y = r->parent;
    335     while(y != NIL && r == y->left){
    336         r = y;
    337         y = y->parent;
    338     }
    339     return y;
    340 }
    341 
    342 void Inorder_RB_Tree_Walk(RB_TreeNode *x){
    343     if(x != NIL){
    344         Inorder_RB_Tree_Walk(x->left);
    345         printf("%d : ", x->value);
    346         if(x->color == 1)
    347             printf("red ");
    348         else
    349             printf("black ");
    350         Inorder_RB_Tree_Walk(x->right);
    351     }
    352 }
    353 
    354 void free_men(RB_TreeNode *x){
    355     if(x != NIL){
    356         free_men(x->left);
    357         free_men(x->right);
    358         free(x);
    359     }
    360 }
    View Code

    PS:吐槽来了。。。以后要多更新博客,打算把当天学到有感觉的东西记录下来。。。算导可能更新会有点慢(已经两个星期才一篇。。。太弱菜了,越到最后要好好磨)。。。打算写SICP学习记录和Linux学校记录。。。。。好好努力吧!!!博客能更新真开心。。。。。

  • 相关阅读:
    Windows 08 R2_组策略
    Nginx常用配置实例(4)
    Nginx日常维护操作(3)
    Nginx配置文件(2)
    Nginx概述和安装(1)
    Zabbix实战-简易教程--通过公众平台企业号发送短信
    HDFS ZKFC自动切换原理分析
    HDFS ZKFC自动切换原理分析
    DataNode启动优化改进:磁盘检测并行化
    DataNode启动优化改进:磁盘检测并行化
  • 原文地址:https://www.cnblogs.com/alan-forever/p/3437306.html
Copyright © 2011-2022 走看看