红黑树

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

红黑树是许多“平衡”搜索树的一种,可以保证在最坏情况下基本动态集合操作的时间复杂度为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

 

posted @ 2015-07-27 19:38  Runnyu  阅读(591)  评论(0编辑  收藏  举报