红黑树是许多“平衡”搜索树的一种,可以保证在最坏情况下基本动态集合操作的时间复杂度为O(lgn)。
红黑树的性质
红黑树是一棵二叉搜索树,它在每个结点上增加了一个存储位来表示结点的颜色,可以是RED和BLACK。
通过对各个结点的颜色进行约束,红黑树确保没有一条路径会比其它路径长出两倍,因此是近似于平衡的。
一个红黑树是满足下面红黑性质的二叉搜索树:
1.每个结点或是红色的,或是黑色的
2.根节点是黑色的
3.每个叶结点(NIL)是黑色的
4.如果一个结点是红色的,则它的两个子结点都是黑色的
5.对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
下面是一棵红黑树的例子
为了便于处理红黑树代码的边界条件,使用一个哨兵来代表NIL。
旋转
搜索树操作TREE-INSERT和TREE-DELETE会对树做修改,结果可能违反了红黑性质。为了维护这些性质,必须改变树中某些结点的颜色以及指针结构。
指针结构的修改是通过旋转来完成的,下面给出了两种旋转:左旋和右旋
下面是LEFT-ROTATE的伪代码,假设x.right≠T.nil且根结点的父结点为T.nil。
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
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
下面是LEFT-ROTATE操作修改二叉搜索树的例子。
RIGHT-ROTATE操作的代码是对称的。
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
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
插入
我们需要对二叉平衡树的插入过程略作修改。我们需要一个辅助程序RB-INSERT-FIXUP来对结点重新着色并选择。
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
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)
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)来保持红黑性质。
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
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
下面给出一个范例,显示在一棵红黑树上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的双亲的相应孩子
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
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
过程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的右结点。
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
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)
删除结点z之后,如果y.original-color==BALCK,RB-DELETE调用一个辅助过程RB-DELETE-FIXUP,该过程通过改变颜色和执行旋转来恢复红黑性质
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
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
下面给出代码中的4种情况
1.x的兄弟结点w是红色的
2.x的兄弟结点w是黑色的,而且w的两个子结点都是黑色的
3.x的兄弟结点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的
4.x的兄弟结点w是黑色的,且w的右孩子是红色的
实现和测试代码
因为红黑色应用比较广,所有决定把它的操作封装成一个类。跟伪代码有一点不同的是:没有设置哨兵nil,很多函数都加上判断是否为NULL。
没有处理查找不到跟删除没有的元素的代码。
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
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 };
测试代码
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
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 }