数据结构之红黑树

  红黑树(Red Black Tree) 是一种自平衡二叉查找树

红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,
从而获得较高的查找性能。
它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的:
它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。(度娘)
C++ stl里面的set,map底层就是用红黑树实现的。
红黑树具体的插入删除原理请参考<<算法导论>> 维基上面也讲得不错。
反正插入过程就是要解决"红-红"冲突,删除就是要解决"黑-黑"冲突。
下面是按照理我解的方式来实现红黑树,我只写了插入和删除过程,

其它的操作和普通的二叉树没多大区别。。
分享一个可以演示红黑树各种操作的网页:

https://www.cs.usfca.edu/~galles/visualization/RedBlack.html

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstdlib>
  4 #include<cstring>
  5 #include<string>
  6 #include<cstdio>
  7 #include<vector>
  8 #include<ctime>
  9 const int RED= 1;
 10 const int BLACK = 0;
 11 struct Node {
 12     int data;
 13     bool color;
 14     Node *fa, *left, *right;
 15     Node() :color(BLACK), data(0){ fa = left = right = NULL; }
 16     Node(int _v, Node *p) :data(_v), color(RED){ fa = left = right = p; }
 17 };
 18 struct RedBlackTree{
 19     Node *root, *null;
 20     void init(){
 21         null = new Node();
 22         root = null;
 23     }
 24     /*
 25     *
 26     *
 27     *
 28     *    对红黑树的节点(x)进行右旋转
 29     *
 30     *          px                            px
 31     *          /                             /
 32     *  ->    x                             y
 33     *       / \                           /  34     *  ->  y   rx     --(右旋)-->        ly   x
 35     *      / \                               /  36     *     ly  ry                            ry  rx
 37     *
 38     *
 39     *
 40     */
 41     //   旋转操作可以不传递引用(reference)话说传引用会快一些。。。
 42     void rotate_right(Node* &x) {
 43         Node *y = x->left;
 44         x->left = y->right;
 45         if (y->right != null) y->right->fa = x;
 46         y->fa = x->fa;
 47         if (x->fa == null) root = y;
 48         else if (x->fa->left == x) x->fa->left = y;
 49         else x->fa->right = y;
 50         y->right = x;
 51         x->fa = y;
 52     }
 53     //   左旋同上
 54     void rotate_left(Node* &x) {
 55         Node *y = x->right;
 56         x->right = y->left;
 57         if (y->left != null) y->left->fa = x;
 58         y->fa = x->fa;
 59         if (x->fa == null) root = y;
 60         else if (x->fa->left == x) x->fa->left = y;
 61         else x->fa->right = y;
 62         y->left = x;
 63         x->fa = y;
 64     }
 65     Node *find(Node *x, int &data) {
 66         while (x != null && x->data != data) {
 67             x = data < x->data ? x->left : x->right;
 68         }
 69         return x;
 70     }
 71     void insert(int v) {
 72         Node *x = root;
 73         Node *y = null;
 74         while (x != null) {        // 先找到带插入节点的位置,y保存该路径上最后一个节点
 75             y = x;
 76             x = v < x->data ? x->left : x->right;
 77         }
 78         x = new Node(v, null);
 79         if (y != null) {           // 确定新节点x与其父节点y的大小关系,再将它们连起来
 80             if (v < y->data) y->left = x;
 81             else y->right = x;
 82         } else {
 83             root = x;
 84         }
 85         x->fa = y;
 86         insert_fix(x);
 87     }
 88     void insert_fix(Node* &x) {
 89         while (x->fa->color != BLACK) {
 90             //    x为当前节点
 91             //    par x的父节点,Gp x的祖父节点, uncle x的叔叔节点
 92             Node *par = x->fa, *Gp = par->fa, *uncle = null;
 93             if (par == Gp->left) {                // 若父节点是祖父的左孩子
 94                 uncle = Gp->right;                // 叔叔为祖父的右孩子
 95                 if (uncle->color == RED) {        // 若叔叔颜色为红色
 96             //    Case1:父亲和叔叔节点变为红色,祖父颜色变为红色
 97             //    将祖父节点设置为当前节点,再继续(因为Gp由黑变成红色,Gp的父节点可能为红色)
 98             //    所以要继续向上递归调整
 99                     par->color = BLACK;
100                     Gp->color = RED;
101                     uncle->color = BLACK;
102                     x = Gp;
103                 } else if (x == par->right) {
104             //    Case2:x为右孩子,将父节点设置为当前节点,再进行左旋
105             //    即变成Case3:
106                     rotate_left(x = par);
107                 } else {
108             //    Case3:x,父节点,祖父节点三者共线(左侧)
109             //    将祖父节点改为红色,父节点改为黑色
110             //    此时调整完成,红黑树性质得到恢复
111                     Gp->color = RED;
112                     par->color = BLACK;
113                     rotate_right(Gp);
114                 }
115             } else {
116             //    同上,只是由左变成右而已
117                 uncle = Gp->left;
118                 if (uncle->color == RED) {
119                     par->color = BLACK;
120                     Gp->color = RED;
121                     uncle->color = BLACK;
122                     x = Gp;
123                 } else if (x == par->left) {
124                     rotate_right(x = par);
125                 } else {
126                     Gp->color = RED;
127                     par->color = BLACK;
128                     rotate_left(Gp);
129                 }
130             }
131         }
132         //    将根置为黑色
133         root->color = BLACK;
134     }
135     void del(int data){
136         //    先找到待删除的节点
137         Node *z = find(root, data);
138         //    若没找到直接退出
139         if (z == null) return;
140         Node *y = z, *x = null;
141         //    若z的左右孩子均不为空,则用y保存z的右子树中最小的节点
142         if (z->left != null && z->right != null) {
143             y= z->right;
144             while (y->left != null) y = y->left;
145         }
146         //    此时y只有左子树,或只有右子树,或左右子树均不存在
147         //    x可能为 null !!!
148         x = y->left != null ? y->left : y->right;
149         //    将x与y的父节点连接起来,这里可能会改变null节点的fa指针,但没有关系的。。。
150         x->fa = y->fa;
151         if (y->fa == null) root = x;
152         //    确定y节点与其父亲的左右关系
153         else  if (y->fa->left == y) y->fa->left = x;
154         else y->fa->right = x;
155         //    若z不等于y 拷贝数据,y才是真正要删除的节点
156         if (z != y) z->data = y->data;
157         //    如果删除的节点y的颜色为黑色,对树进行调整使得树满足红黑树的要求
158         if (y->color == BLACK) del_fix(x);
159         //    释放y
160 #ifdef DE_BUG
161         delete y;
162 #endif
163     }
164     void del_fix(Node* &x) {
165         while (x != root && x->color == BLACK) {
166             //    x为当前节点
167             //    par x的父节点, sibling x的兄弟节点
168             Node *par = x->fa, *sibling = null;
169             //    x为父节点的左孩子
170             if (par->left == x){
171             //    兄弟节点则为父节点的右孩子
172                 sibling = par->right;
173             //    若兄弟节点的颜色为红色
174                 if (sibling->color == RED) {
175             //    Case1:将兄弟节点染成黑色,父节点染成红色
176                     sibling->color = BLACK;
177                     par->color = RED;
178             //    父节点左旋
179                     rotate_left(par);
180             //    重新设置兄弟节点
181                     sibling = par->right;
182                 } else
183             //    Case2:兄弟节点为黑色,兄弟节点的左右孩子均为黑色
184                 if (sibling->left->color == BLACK && sibling->right->color == BLACK) {
185             //    将兄弟节点染成红色
186                     sibling->color = RED;
187             //    将父节点变成当前节点
188                     x = par;
189                 } else {
190             //    Case3:兄弟节点为黑色,兄弟节点的右孩子为黑色,左孩子为红色
191                     if (sibling->right->color == BLACK) {
192             //    将兄弟节点的左孩子染成黑色
193                         sibling->left->color = BLACK;
194             //    将兄弟节点染成红色
195                         sibling->color = RED;
196             //    将兄弟节点右旋
197                         rotate_right(sibling);
198             //    重新设置兄弟节点
199                         sibling = par->right;
200                     }
201             //    Case4:兄弟节点为黑色,兄弟节点的右孩子为红色,左孩子为任意颜色
202             //    将兄弟节点染成父节点的颜色
203                     sibling->color = par->color;
204             //    父节点染成黑色
205                     par->color = BLACK;
206             //    兄弟节点的右孩子染成黑色
207                     sibling->right->color = BLACK;
208             //    将父节点左旋,跳出循环。
209                     rotate_left(par);
210                     break;
211                 }
212             } else {
213             //    同上,只是由左变成右而已
214                 sibling = par->left;
215                 if (sibling->color == RED) {
216                     sibling->color = BLACK;
217                     par->color = RED;
218                     rotate_right(par);
219                     sibling = par->left;
220                 } else
221                 if (sibling->left->color == BLACK && sibling->right->color == BLACK) {
222                     sibling->color = RED;
223                     x = par;
224                 } else {
225                     if (sibling->left->color == BLACK){
226                         sibling->right->color = BLACK;
227                         sibling->color = RED;
228                         rotate_left(sibling);
229                         sibling = par->left;
230                     } else {
231                         sibling->color = par->color;
232                         par->color = BLACK;
233                         sibling->left->color = BLACK;
234                         rotate_right(par);
235                         break;
236                     }
237                 }
238             }
239         }
240         x->color = BLACK;
241     }
242 }rbt_tree;
243 int main() {
244 //  以下为测试
245     int a = clock();
246     rbt_tree.init();
247     for (int i = 0; i < 2000000; i++) {
248         rbt_tree.insert(i);
249     }
250 #ifdef DE_BUG
251     for (int i = 0; i < 2000000; i++) {
252         rbt_tree.del(i);
253     }
254 #endif
255     rbt_tree.del(4);
256     printf("%d\n", clock() - a);
257     return 0;
258 }

下面是以bzoj3224/Tyvj 1728普通平衡树 为例实现了红黑树的各种操作
增加了size域(子树的大小),cnt域(关键字出现的次数)。
不想吐槽了,wa的快吐了%>_<% 由于插入,删除均用非递归方式实现的,

导致维护size,cnt域太麻烦了。。。
自己对拍了数据有输出1w多行才出错的(这咋调试啊 (*>﹏<*) )。还好最后发现了bug。

程序只通过了oj的数据,不知道还有没有隐含的bug →_→ 写的很搓懒得注释了。。。
欢迎各位游客批评指正O(∩_∩)O~~

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstdlib>
  4 #include<cstring>
  5 #include<string>
  6 #include<cstdio>
  7 #include<vector>
  8 #include<ctime>
  9 const int Max_N = 110000;
 10 struct Node {
 11     int data, s, c;
 12     bool color;
 13     Node *fa, *ch[2];
 14     inline void set(int _v, bool _color, int i, Node *p) {
 15         data = _v, color = _color, s = c = i;
 16         fa = ch[0] = ch[1] = p;
 17     }
 18     inline void push_up() {
 19         s = ch[0]->s + ch[1]->s + c;
 20     }
 21     inline void push_down() {
 22         for (Node *x = this; x->s; x = x->fa) x->s--;
 23     }
 24     inline int cmp(int v) const {
 25         return data == v ? -1 : v > data;
 26     }
 27 };
 28 struct RedBlackTree {
 29     int top;
 30     Node *root, *null;
 31     Node stack[Max_N], *tail, *store[Max_N];
 32     void init() {
 33         tail = &stack[0];
 34         null = tail++;
 35         null->set(0, 0, 0, NULL);
 36         root = null;
 37         top = 0;
 38     }
 39     inline Node *newNode(int v) {
 40         Node *p = null;
 41         if (!top) p = tail++;
 42         else p = store[--top];
 43         p->set(v, 1, 1, null);
 44         return p;
 45     }
 46     inline void rotate(Node* &x, bool d ) {
 47         Node *y = x->ch[!d];
 48         x->ch[!d] = y->ch[d];
 49         if (y->ch[d]->s) y->ch[d]->fa = x;
 50         y->fa = x->fa;
 51         if (!x->fa->s) root = y;
 52         else x->fa->ch[x->fa->ch[0] != x] = y;
 53         y->ch[d] = x;
 54         x->fa = y;
 55         y->s = x->s;
 56         x->push_up();
 57     }
 58     inline void insert(int v) {
 59         Node *x = root, *y = null;
 60         while (x->s) {
 61             x->s++, y = x;
 62             int d = x->cmp(v);
 63             if (-1 == d) {
 64                 x->c++;
 65                 return;
 66             }
 67             x = x->ch[d];
 68         }
 69         x = newNode(v);
 70         if (y->s) y->ch[v > y->data] = x;
 71         else root = x;
 72         x->fa = y;
 73         insert_fix(x);
 74     }
 75     inline void insert_fix(Node* &x) {
 76         while (x->fa->color) {
 77             Node *par = x->fa, *Gp = par->fa;
 78             bool d = par == Gp->ch[0];
 79             Node *uncle = Gp->ch[d];
 80             if (uncle->color) {
 81                 par->color = uncle->color = 0;
 82                 Gp->color = 1;
 83                 x = Gp;
 84             } else if (x == par->ch[d]) {
 85                 rotate(x = par, !d);
 86             } else {
 87                 Gp->color = 1;
 88                 par->color = 0;
 89                 rotate(Gp, d);
 90             }
 91         }
 92         root->color = 0;
 93     }
 94     inline Node *find(Node *x, int data) {
 95         while (x->s && x->data != data) x = x->ch[x->data < data];
 96         return x;
 97     }
 98     inline void del_fix(Node* &x) {
 99         while (x != root && !x->color) {
100             bool d = x == x->fa->ch[0];
101             Node *par = x->fa, *sibling = par->ch[d];
102             if (sibling->color) {
103                 sibling->color = 0;
104                 par->color = 1;
105                 rotate(x->fa, !d);
106                 sibling = par->ch[d];
107             } else if (!sibling->ch[0]->color && !sibling->ch[1]->color) {
108                 sibling->color = 1, x = par;
109             } else {
110                 if (!sibling->ch[d]->color) {
111                     sibling->ch[!d]->color = 0;
112                     sibling->color = 1;
113                     rotate(sibling, d);
114                     sibling = par->ch[d];
115                 }
116                 sibling->color = par->color;
117                 sibling->ch[d]->color = par->color = 0;
118                 rotate(par, !d);
119                 break;
120             }
121         }
122         x->color = 0;
123     }
124     inline void del(int data) {
125         Node *z = find(root, data);
126         if (!z->s) return;
127         if (z->c > 1) {
128             z->c--;
129             z->push_down();
130             return;
131         }
132         Node *y = z, *x = null;
133         if (z->ch[0]->s && z->ch[1]->s) {
134             y = z->ch[1];
135             while (y->ch[0]->s) y = y->ch[0];
136         }
137         x = y->ch[!y->ch[0]->s];
138         x->fa = y->fa;
139         if (!y->fa->s) root = x;
140         else y->fa->ch[y->fa->ch[1] == y] = x;
141         if (z != y) z->data = y->data, z->c = y->c;
142         y->fa->push_down();
143         for (Node *k = y->fa; y->c > 1 && k->s && k != z; k = k->fa) k->s -= y->c - 1;
144         if (!y->color) del_fix(x);
145         store[top++] = y;
146     }
147     inline void kth(int k) {
148         int t;
149         Node *x = root;
150         for (; x->s;) {
151             t = x->ch[0]->s;
152             if (k <= t) x = x->ch[0];
153             else if (t + 1 <= k && k <= t + x->c) break;
154             else k -= t + x->c, x = x->ch[1];
155         }
156         printf("%d\n", x->data);
157     }
158     inline void rank(int v) {
159         int t, cur = 0;
160         Node *x = root;
161         for (; x->s;) {
162             t = x->ch[0]->s;
163             if (v == x->data) break;
164             else if (v < x->data) x = x->ch[0];
165             else cur += t + x->c, x = x->ch[1];
166         }
167         printf("%d\n", cur + t + 1);
168     }
169     inline void succ(int v) {
170         int ret = 0;
171         Node *x = root;
172         while (x->s) {
173             if (x->data > v) ret = x->data, x = x->ch[0];
174             else x = x->ch[1];
175         }
176         printf("%d\n", ret);
177     }
178     inline void pred(int v) {
179         int ret = 0;
180         Node *x = root;
181         while (x->s) {
182             if (x->data < v) ret = x->data, x = x->ch[1];
183             else x = x->ch[0];
184         }
185         printf("%d\n", ret);
186     }
187 }rbt;
188 int main() {
189 #ifdef LOCAL
190     freopen("in.txt", "r", stdin);
191     freopen("out.txt", "w+", stdout);
192 #endif
193     rbt.init();
194     int n, op, v;
195     scanf("%d", &n);
196     while (n--) {
197         scanf("%d %d", &op, &v);
198         if (1 == op) rbt.insert(v);
199         else if (2 == op) rbt.del(v);
200         else if (3 == op) rbt.rank(v);
201         else if (4 == op) rbt.kth(v);
202         else if (5 == op) rbt.pred(v);
203         else rbt.succ(v);
204     }
205     return 0;
206 }

时间: 2024-08-02 15:14:33

数据结构之红黑树的相关文章

D&amp;F学数据结构系列——红黑树

红黑树 定义:一棵二叉查找树如果满足下面的红黑性质,则为一棵红黑树: 1)每个结点不是红的就是黑的 2)根结点是黑的 3)每个叶结点是黑的 4)如果一个结点是红的,它的两个儿子都是黑的(即不可能有两个连续的红色结点) 5)对于每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点 性质: 这些约束确保了红黑树的关键特性: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长.结果是这个树大致上是平衡的.因为操作比如插入.删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上

nginx学习九 高级数据结构之红黑树ngx_rbtree_t

1红黑树简介 先来看下算法导论对R-B Tree的介绍: 红黑树,一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black. 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平的. 红黑树,作为一棵二叉查找树,满足二叉查找树的一般性质.下面,来了解下 二叉查找树的一般性质. 二叉查找树 二叉查找树,也称有序二叉树(ordered binary tree),或已排序二叉树(sorted binary tree

数据结构:红黑树解析

本文参考:Google.算法导论.STL源码剖析.计算机程序设计艺术. 推荐阅读: Left-Leaning Red-Black Trees, Dagstuhl Workshop on Data Structures, Wadern, Germany, February, 2008,直接下载:http://www.cs.princeton.edu/~rs/talks/LLRB/RedBlack.pdf. 本文的github优化版:https://github.com/julycoding/The

数据结构--树--红黑树

R-B Tree简介 R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树.红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black). 红黑树的特性:(1)每个节点或者是黑色,或者是红色.(2)根节点是黑色.(3)每个叶子节点(NIL)是黑色. [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!](4)如果一个节点是红色的,则它的子节点必须是黑色的.(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点.

数据结构之红黑树(三)——删除操作

删除一个节点相同有可能改变树的平衡性,并且,删除所造成的不平衡性比插入所造成的平衡性的修正更加复杂. 化繁为简是算法分析中一个经常使用的方法.以下我们将欲删除节点分为三大类:欲删除节点为叶子节点.欲删除节点仅仅有一个子节点和欲删除有两个子节点. 而欲删除节点有两种可能的颜色,也须要分别对待. 为简化讨论,我们以欲删除节点在左側的情况为例进行修正,假设欲删除节点在右側,进行镜像地修正操作就可以. 1. 欲删除节点是叶子节点 1.1 欲删除节点为红色,父节点必为黑色.必无兄弟节点. 仅仅有下图所看到

数据结构之——红黑树

红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是red或black.通过对任何一条从根到叶子简单路径上的颜色来约束,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡. 红黑树是满足下面红黑性质的二叉搜索树: 每个节点,不是红色就是黑色的 根节点是黑色的 如果一个节点是红色的,则它的两个子节点是黑色的(没有连续的红节点) 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点.(每条路径的黑色节点的数量相等) 这里分析一下为什么红黑树能保证

【数据结构】红黑树

红黑树 目的 在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能. 效率 查找,插入和 删除 时间复杂度:O(log n) ,n 是树中元素数目. 性质 节点是红色或黑色. 根节点是黑色. 每个叶节点(NIL节点,空节点)是黑色的. 每个红色节点的两个子节点都是黑色.(从每个叶子到根的所有路径上不能有两个连续的红色节点) 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点. 版权声明:本文为博主原创文章,未经博主允许不得转载.

数据结构之红黑树(二)——插入操作

插入或删除操作,都有可能改变红黑树的平衡性,利用颜色变化与旋转这两大法宝就可应对所有情况,将不平衡的红黑树变为平衡的红黑树. 在进行颜色变化或旋转的时候,往往要涉及祖孙三代节点:X表示操作的基准节点,P代表X的父节点,G代表X的父节点的父节点. 我们先来大体预览一下插入的过程: 1.沿着树查找插入点,如果查找过程中发现某个黑色节点的两个子节点都是红色,则执行一次颜色变换(父节点变为红色,而两个红色子节点变为黑色). 2.第1步中,不会改变子树的黑色高度,但是可能会出现颜色冲突(红-红颜色冲突),

linux内核数据结构之红黑树

首先我先回顾一下二叉树 然后回顾一下二叉搜索树 下面是重头戏 自平衡二叉搜索树满足二叉搜索树的条件.即每个节点左边的节点值都要比自己小,然后满足平衡,即树(包括子树)的末尾节点深度相差小于1,这样的树称为平衡二叉搜索树 最后红黑树 红黑树有着插入,删除,搜索非常快的优点,特别是插入和删除要比平衡二叉搜索树要快,所以在有频繁的插入和删除操作的情况下,使用红黑树进行存储是非常有效的. linux内核中提供了红黑树的基本算法,我们只需要构造自己的插入,删除,和搜索函数就可以根据自己的需求使用红黑树了.