红黑树

来源:互联网 发布:js判断偶数 编辑:程序博客网 时间:2024/06/15 08:25
之前介绍了二叉搜索树。它的操作的时间复杂度跟树的高度有关,如果树的高度较高的时候,这些集合操作可能并不比在链表执行得快。

红黑树是许多“平衡”搜索树的一种,可以保证在最坏情况下基本动态集合操作的时间复杂度为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.left10     x.p.left=y11 else x.p.right=y12 y.left=x             //put x on y'left13 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.left10     y.p.left=x11 else y.p.right=x12 x.right=y                 //put y on x's left13 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.right10 z.p=y11 if y==T.nil12     T.root=z13 else y.right=z14 z.left=T.nil15 z.right=T.nil16 z.color=RED17 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.p10         else 11             if z==z.p.right             //case 212                 z=z.p13                 LEFT-ROTATE(T,z)14            else15                 z.p.color=BALCK      //case 316                 z.p.p.color=RED17                 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.nil3     T.root=v4 else if u==u.p.left5     u.p.left=v6 else u.p.right=v7 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.left10     RB-TRANSPLANT(T,z,z.left)11 //有两个子结点12 else y=TREE-MINIMUM(z.right)13     y-original-color=y.color14     x=y.right15     if y.p==z16         x.p=y17     else RB-TRANSPLANT(T,y,y.right)18         y.right=z.right19         y.right.p=y20     RB-TRANSPLANT(T,z,y)21     y.left=z.left22     y.left.p=y23     y.color=z.color24 if y.original-color==BALCK25     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 210             w.color=RED11             x=x.p12         else13             if w.right.color=BALCK    //case 314                 w.left.color=BALCK15                 w.color=RED16                 RIGHT-ROTATE(T,w)17                 w=x.p.right18             else                               //case 419                 w.color=x.p.color20                 x.p.color=BALCK21                 w.right.color=BALCK22                 LEFT-ROTATE(T,x.p)23                 x=T.root24     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             else102             {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                     else119                     {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                 else177                 {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             else195             {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                 else211                 {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             else256                 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         else265             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             else294                 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         else315         {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             else322             {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

 

0 0