zoukankan      html  css  js  c++  java
  • 红黑树

    之前介绍了二叉搜索树。它的操作的时间复杂度跟树的高度有关,如果树的高度较高的时候,这些集合操作可能并不比在链表执行得快。

    红黑树是许多“平衡”搜索树的一种,可以保证在最坏情况下基本动态集合操作的时间复杂度为O(lgn)。

    红黑树的性质

    红黑树是一棵二叉搜索树,它在每个结点上增加了一个存储位来表示结点的颜色,可以是RED和BLACK。

    通过对各个结点的颜色进行约束,红黑树确保没有一条路径会比其它路径长出两倍,因此是近似于平衡的。

    一个红黑树是满足下面红黑性质的二叉搜索树:

    1.每个结点或是红色的,或是黑色的

    2.根节点是黑色的

    3.每个叶结点(NIL)是黑色的

    4.如果一个结点是红色的,则它的两个子结点都是黑色的

    5.对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点

    下面是一棵红黑树的例子

    为了便于处理红黑树代码的边界条件,使用一个哨兵来代表NIL。

    旋转

    搜索树操作TREE-INSERT和TREE-DELETE会对树做修改,结果可能违反了红黑性质。为了维护这些性质,必须改变树中某些结点的颜色以及指针结构。

    指针结构的修改是通过旋转来完成的,下面给出了两种旋转:左旋和右旋

    下面是LEFT-ROTATE的伪代码,假设x.right≠T.nil且根结点的父结点为T.nil。

     1 LEFT-ROTATE(T,x)
     2 y=x.right            //set y
     3 x.right=y.left       //trun y's left subtree into x'right subtree
     4 if y.left≠T.nil
     5     y.left.p=x
     6 y.p=x.p              //link x' parent to y
     7 if x.p==T.nil
     8     T.root=y
     9 else if x==x.p.left
    10     x.p.left=y
    11 else x.p.right=y
    12 y.left=x             //put x on y'left
    13 x.p=y
    View Code

    下面是LEFT-ROTATE操作修改二叉搜索树的例子。

    RIGHT-ROTATE操作的代码是对称的。

     1 RIGHT-ROTATE(T,y)
     2 x=y.left                    //set x
     3 y.left=x.right            //turn x's right subtree into y's right subtree
     4 if x.right≠T.nil
     5     x.right.p=y
     6 x.p=y.p                    //link y's parent to x
     7 if y.p==T.nil
     8     T.root=x
     9 else if y==y.p.left
    10     y.p.left=x
    11 else y.p.right=x
    12 x.right=y                 //put y on x's left
    13 y.p=x
    View Code

    插入

    我们需要对二叉平衡树的插入过程略作修改。我们需要一个辅助程序RB-INSERT-FIXUP来对结点重新着色并选择。

     1 RB-INSERT(T,z)
     2 y=T.nil
     3 x=T.root
     4 while x≠T.nil
     5     y=x
     6     if z.key<x.key
     7         x=x.left
     8     else 
     9         x=x.right
    10 z.p=y
    11 if y==T.nil
    12     T.root=z
    13 else y.right=z
    14 z.left=T.nil
    15 z.right=T.nil
    16 z.color=RED
    17 RB-INSERT-FIXUP(T,z)
    View Code

    TREE-INSERT和RB-INSERT之间有4处不同:

    1.TREE-INSERT内所有的NIL都被T.nil代替

    2.RB-INSERT将z.left和z.right置为T.nil

    3.将z着为红色

    4.因为z着为红色可能违反红黑性质,所有调用RB-INSERT-FIXUP(T,z)来保持红黑性质。

     1 RB-INSERT-FIXUP(T,z)
     2 while z.p.color==RED
     3     if z.p==z.p.p.left
     4         y=z.p.p.right
     5         if y.color==RED               //case 1
     6             z.p.color=BALCK    
     7             y.color=BALCK
     8             y.p.p.color=RED
     9             z=z.p.p
    10         else 
    11             if z==z.p.right             //case 2
    12                 z=z.p
    13                 LEFT-ROTATE(T,z)
    14            else
    15                 z.p.color=BALCK      //case 3
    16                 z.p.p.color=RED
    17                 RIGHT-ROTATE(T,z.p.p)
    18    else(same as then clause with "right" and "left" exchanged)
    19 T.root.color=BALCK            
    View Code

    下面给出一个范例,显示在一棵红黑树上RB-INSERT-FIXUP如何操作

    RB-INSERT-FIXUP(T,z)过程一直把z迭代向上,直到z结点的父结点为黑色,每次有3种情况:

    (a)判断z的叔结点,如果为红色,应用case 1。

    (b)如果z的叔结点为黑色,而且z是的父结点的右孩子,应用case 2

    (c)如果z的叔结点为黑色,而且z是的父结点的左孩子,应用case 3

    删除

    修改二叉平衡树中TREE-DELETE调用的子过程TRANSPLANT,并将其应用到红黑树上。

    RB-TRANSPLANT用一棵以v为根的子树来替换一棵以u为根的子树:结点u的双亲就变为结点v的双亲,并且最后v成为u的双亲的相应孩子

    1 RB-TRANSPLANT(T,u,v)
    2 if u.p==T.nil
    3     T.root=v
    4 else if u==u.p.left
    5     u.p.left=v
    6 else u.p.right=v
    7 v.p=u.p
    View Code

    过程RB-DELETE与TREE-DELETE类似,只是多了几行伪代码用来记录结点y的踪迹,y有可能导致红黑性质的破坏。

    1.当要删除结点z,且此时z有少于两个子结点时,z从树中删除,并将这个孩子(可能为T.nil)提升到树中z的位置上。

    2.当z有两个子结点时,y应该是z的后继,并且y将移至树中的z位置。

    (a)如果y是z的右孩子,那么用y替换z,原来z的左孩子称为y的左孩子(可以证明原来y的左孩子必为T.nil)。

    (b)如果y并不是z的右孩子。则先用y的右孩子替换y,然后用y替换z

    如果z的子结点少于两个时,使用y记录z的颜色,x用来记录z的左或右结点。

    如果z的子结点数目为两个的时候,y用来记录替换z的结点,x用来记录y的右结点。

     1 RB-DELETE(T,z)
     2 y=z
     3 y-original-color=y.color
     4 //只有一个子结点
     5 if z.left==T.nil
     6     x=z.right
     7     RB-TRANSPLANT(T,z,z.right)
     8 else if z.right==T.nil
     9     x=z.left
    10     RB-TRANSPLANT(T,z,z.left)
    11 //有两个子结点
    12 else y=TREE-MINIMUM(z.right)
    13     y-original-color=y.color
    14     x=y.right
    15     if y.p==z
    16         x.p=y
    17     else RB-TRANSPLANT(T,y,y.right)
    18         y.right=z.right
    19         y.right.p=y
    20     RB-TRANSPLANT(T,z,y)
    21     y.left=z.left
    22     y.left.p=y
    23     y.color=z.color
    24 if y.original-color==BALCK
    25     RB-DELETE-FIXUP(T,x)
    View Code

    删除结点z之后,如果y.original-color==BALCK,RB-DELETE调用一个辅助过程RB-DELETE-FIXUP,该过程通过改变颜色和执行旋转来恢复红黑性质

     1 while x≠T.root and x.color==BALCK
     2     if x==x.p.left
     3         w=x.p.right
     4         if w.color=RED                 //case 1
     5             w.color=BALCK
     6             x.p.color=RED
     7             LEFT-ROTATE(T,x.p)
     8             w=x.p.right
     9         if w.left.color==BALCK and w.right.color==BALCK  //case 2
    10             w.color=RED
    11             x=x.p
    12         else
    13             if w.right.color=BALCK    //case 3
    14                 w.left.color=BALCK
    15                 w.color=RED
    16                 RIGHT-ROTATE(T,w)
    17                 w=x.p.right
    18             else                               //case 4
    19                 w.color=x.p.color
    20                 x.p.color=BALCK
    21                 w.right.color=BALCK
    22                 LEFT-ROTATE(T,x.p)
    23                 x=T.root
    24     else (same as then clause with "right" and "left" exchanged)
    25 x.color=BLACK
    View Code

    下面给出代码中的4种情况

    1.x的兄弟结点w是红色的

    2.x的兄弟结点w是黑色的,而且w的两个子结点都是黑色的

    3.x的兄弟结点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的

    4.x的兄弟结点w是黑色的,且w的右孩子是红色的

    实现和测试代码

    因为红黑色应用比较广,所有决定把它的操作封装成一个类。跟伪代码有一点不同的是:没有设置哨兵nil,很多函数都加上判断是否为NULL。

    没有处理查找不到跟删除没有的元素的代码。

      1 #define RED 0
      2 #define BLACK 1
      3 
      4 #include <stdbool.h>
      5 #include <iostream>
      6 using namespace std;
      7  
      8 
      9 struct node
     10 {
     11     int key;
     12     int data;
     13     bool color;   
     14     node *left;
     15     node *right;
     16     node *parent;    
     17 };
     18 
     19 class RBTree
     20 {
     21 private:
     22     node *root;
     23     //左旋 
     24     void left_rotate(node *x)
     25     {
     26         node *y=x->right;
     27         x->right=y->left;
     28         if(y->left!=NULL)
     29             y->left->parent=x;
     30         y->parent=x->parent;
     31         if(x->parent==NULL)
     32             root=y;
     33         else if(x==x->parent->left)
     34             x->parent->left=y;
     35         else
     36             x->parent->right=y;
     37         y->left=x;
     38         x->parent=y;
     39     }
     40     //右旋 
     41     void right_rotate(node *y)
     42     {
     43         node *x=y->left;
     44         y->left=y->right;
     45         if(x->right!=NULL)
     46             x->right->parent=y;
     47         x->parent=y->parent;
     48         if(y->parent==NULL)
     49             root=x;
     50         else if(y==y->parent->left)
     51             y->parent->left=x;
     52         else
     53             y->parent->right=x;
     54         x->right=y;
     55         y->parent=x;
     56     }
     57     //找出根结点为t的子树的最小键的结点 
     58     node *tree_minimum(node *t)
     59     {
     60         while(t->left!=NULL)
     61             t=t->left;
     62         return t;
     63     }
     64     //对插入之后进行修正,保持红黑性质 
     65     void rb_insert_fixup(node *z)
     66     {
     67         //当z不是根同时父结点的颜色是red 
     68         while(root!=z&&z->parent->color==RED)
     69         {
     70             //如果z结点的父结点是其父结点的左孩子(如果是右孩子则执行else) 
     71             if(z->parent==z->parent->parent->left)
     72             {
     73                 //叔结点 
     74                 node *y=z->parent->parent->right;
     75                 //如果叔结点不为NULL并且颜色为RED 
     76                 if(y!=NULL&&y->color==RED)
     77                 {
     78                     z->parent->color=BLACK;
     79                     y->color=BLACK;
     80                     z->parent->parent->color=RED;
     81                     z=z->parent->parent;
     82                 }
     83                 //否则执行else 
     84                 else 
     85                 {
     86                     //如果该结点是其父结点的右孩子 
     87                     if(z==z->parent->right)
     88                     {
     89                         z=z->parent;
     90                         left_rotate(z);
     91                     }
     92                     //如果该结点是其父结点的左孩子 
     93                     else
     94                     {
     95                         z->parent->color=BLACK;
     96                         z->parent->parent->color=RED;
     97                         right_rotate(z->parent->parent);
     98                     }
     99                 }    
    100             }
    101             else
    102             {
    103                 node *y=z->parent->parent->left;
    104                 if(y!=NULL&&y->color==RED)
    105                 {
    106                     z->parent->color=BLACK;
    107                     y->color=BLACK;
    108                     z->parent->parent->color=RED;
    109                     z=z->parent->parent;
    110                 }
    111                 else 
    112                 {
    113                     if(z==z->parent->left)
    114                     {
    115                         z=z->parent;
    116                         right_rotate(z);
    117                     }
    118                     else
    119                     {
    120                         z->parent->color=BLACK;
    121                         z->parent->parent->color=RED;
    122                         left_rotate(z->parent->parent);
    123                     }
    124                 }    
    125             }
    126         }
    127         root->color=BLACK;
    128     }
    129     //中序遍历 
    130     void tree_inorder_p(node *t)
    131     {
    132         if(t!=NULL)
    133         {
    134             tree_inorder_p(t->left);
    135             cout<<"key:"<<t->key<<"  data:"<<t->data<<"  color:"<<(t->color?"BLACK":"RED")<<endl;
    136             tree_inorder_p(t->right);
    137         }
    138     } 
    139     //删除结点调用的子过程  用来将一棵以v为根的子树来替换一棵以u为根的子树 
    140     void rb_transplant(node *u,node *v) 
    141     {
    142         //u为根结点 
    143         if(u->parent==NULL)
    144             root=v;
    145         //判断u是其父结点的左孩子还是右孩子 
    146         else if(u==u->parent->left)
    147             u->parent->left=v;
    148         else 
    149             u->parent->right=v;
    150         if(v!=NULL) 
    151             v->parent=u->parent; 
    152     } 
    153     //删除结点之后的修正函数  用来保持红黑性质 
    154     void rb_delete_fixup(node *x)
    155     {
    156         node *w;
    157         while(x!=root&&(x==NULL||x->color==BLACK))
    158         {
    159             //如果x是其父结点的左孩子 
    160             if(x==x->parent->left)
    161             {
    162                 w=x->parent->right;
    163                 if(w->color==RED)
    164                 {
    165                     w->color=BLACK;
    166                     x->parent->color=RED;
    167                     left_rotate(x->parent);
    168                     w=x->parent->right;
    169                 }
    170                 if((w->left==NULL||w->left->color==BLACK)&&
    171                    (w->right==NULL||w->right->color==BLACK))
    172                 {
    173                     w->color=RED;
    174                     x=x->parent;
    175                 } 
    176                 else
    177                 {
    178                     if((w->right==NULL||w->right->color==BLACK))
    179                     {
    180                         if(w->left!=NULL)
    181                             w->left->color=BLACK;
    182                         w->color=RED;
    183                         right_rotate(w);
    184                         w=x->parent->right;
    185                     }
    186                     w->color=x->parent->color;
    187                     x->parent->color=BLACK;
    188                     if(w->right!=NULL)
    189                         w->right->color=BLACK;
    190                     left_rotate(x->parent);
    191                     x=root;
    192                 }
    193             }
    194             else
    195             {
    196                 w=x->parent->left;
    197                 if(w->color==RED)
    198                 {
    199                     w->color=BLACK;
    200                     x->parent->color=RED;
    201                     right_rotate(x->parent);
    202                     w=x->parent->left;
    203                 }
    204                 if((w->left==NULL||w->left->color==BLACK)&&
    205                    (w->right==NULL||w->right->color==BLACK))
    206                 {
    207                     w->color=RED;
    208                     x=x->parent;
    209                 } 
    210                 else
    211                 {
    212                     if((w->left==NULL||w->left->color==BLACK))
    213                     {
    214                         if(w->right!=NULL)
    215                             w->right->color=BLACK;
    216                         w->color=RED;
    217                         left_rotate(w);
    218                         w=x->parent->right;
    219                     }
    220                     w->color=x->parent->color;
    221                     x->parent->color=BLACK;
    222                     if(w->left!=NULL)
    223                         w->left->color=BLACK;
    224                     left_rotate(x->parent);
    225                     x=root;
    226                 }
    227             } 
    228         }
    229         x->color=BLACK;
    230     } 
    231     void delete_tree(node *t)
    232     {
    233         if(t!=NULL)
    234         {
    235             delete_tree(t->left);
    236             delete t;
    237             delete_tree(t->right);
    238         }
    239     }
    240     
    241 public:
    242     RBTree(node *r) {root=r;root->color=BLACK;}
    243     //插入函数 
    244     void tree_insert(node* z)
    245     {
    246         //x是当前结点,y用来记录插入位置的父结点 
    247         node *y=NULL;
    248         node *x=root;
    249         //找到插入结点的父结点,并用y记录起来 
    250         while(x!=NULL)
    251         {
    252             y=x;
    253             if(z->key<x->key)
    254                 x=x->left;
    255             else
    256                 x=x->right;
    257         }
    258         //插入结点 
    259         z->parent=y;
    260         if(y==NULL)
    261             root=z;
    262         else if(z->key<y->key)
    263             y->left=z;
    264         else
    265             y->right=z;
    266         z->right=NULL;
    267         z->left=NULL;
    268         z->color=RED;
    269         rb_insert_fixup(z);
    270     }    
    271     //根据键值来make_node 
    272     static node *make_node(int k,int d)
    273     {
    274         node *z=new node();
    275         z->key=k;
    276         z->data=d;
    277         z->color=RED;
    278         z->left=NULL;
    279         z->right=NULL;
    280         z->parent=NULL;
    281         return z;
    282     }
    283     //中序遍历包装函数 
    284     void tree_inorder(){tree_inorder_p(root);}
    285     //查找函数 
    286     node *tree_search(int key)
    287     {
    288         node *t=root;
    289         while(t!=NULL&&key!=t->key)
    290         {
    291             if(key<t->key)
    292                 t=t->left;
    293             else
    294                 t=t->right;
    295         }
    296         return t;
    297     }
    298     //删除函数
    299     void rb_delete(node *z)
    300     {
    301         node *y=z;
    302         node *x;
    303         int y_original_color=y->color;
    304         if(z->left==NULL)
    305         {
    306             x=z->left;
    307             rb_transplant(z,z->left);
    308         }
    309         else if(z->right==NULL)
    310         {
    311             x=z->left;
    312             rb_transplant(z,z->left);
    313         }
    314         else
    315         {
    316             y=tree_minimum(z->right);
    317             y_original_color=y->color;
    318             x=y->right;
    319             if(y->parent==z)
    320                 x->parent=y;
    321             else
    322             {
    323                 rb_transplant(y,y->right);
    324                 y->right=z->right;
    325                 y->right->parent=y;
    326             } 
    327             rb_transplant(z,y);
    328             y->left=z->left;
    329             y->left->parent=y;
    330             y->color=z->color;
    331         }
    332         if(y_original_color==BLACK)
    333             rb_delete_fixup(x);
    334         delete z;
    335     } 
    336     //析构函数 
    337     ~RBTree()
    338     {
    339         delete_tree(root);
    340     }
    341 };
    View Code

    测试代码

     1 #include <stdbool.h>
     2 #include <iostream>
     3 #include <time.h>
     4 #include <stdlib.h>
     5 using namespace std;
     6 
     7 int main()
     8 {
     9     srand(time(NULL));
    10     RBTree *t=new RBTree(RBTree::make_node(1,20));
    11 
    12     node *n1=RBTree::make_node(2,rand()%100);
    13     node *n2=RBTree::make_node(3,rand()%100);
    14     node *n3=RBTree::make_node(4,rand()%100);
    15     node *n4=RBTree::make_node(5,rand()%100);
    16     t->tree_insert(n1); 
    17     t->tree_insert(n2); 
    18     t->tree_insert(n3); 
    19     t->tree_insert(n4); 
    20         
    21     cout<<"after insert:"<<endl;
    22     t->tree_inorder();
    23     t->rb_delete(n2);
    24     cout<<endl;
    25     cout<<"after delete:"<<endl;
    26     t->tree_inorder();
    27     cout<<endl;
    28 
    29     cout<<"serach key=2 data="<<t->tree_search(2)->data<<endl;
    30     delete t;
    31     return 0;
    32 }
    View Code
  • 相关阅读:
    Sublime Text 2 && MinGW G++ On Windows
    [zz]linux IO(function open read write close)
    PHP的基本常识(1)
    helloworld.c 的一次系统旅行(1) 读书笔记
    PHP 仿博客园 个人博客(1)
    配置 .htaccess 单点入口
    仿博客园个人博客(3)基本完成
    PHP的基本常识(2)
    JQuery 获得所有表单值
    PHP 仿博客园 个人博客(2)
  • 原文地址:https://www.cnblogs.com/runnyu/p/4679279.html
Copyright © 2011-2022 走看看