zoukankan      html  css  js  c++  java
  • 数据结构之红黑树

      红黑树(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
    ", clock() - a);
    257     return 0;
    258 }
    View Code

    下面是以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
    ", 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
    ", 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
    ", 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
    ", 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 }
    View Code
    By: GadyPu 博客地址:http://www.cnblogs.com/GadyPu/ 转载请说明
  • 相关阅读:
    解决Struts中文乱码问题总结
    也谈创业企业CEO该拿多少工资
    Codeforces Round #242 (Div. 2) A. Squats
    设计模式之模板方法模式
    hdu2768Cat vs. Dog (反建法,最大独立集)
    Python
    kvm
    linux bin文件制作
    python
    pytho
  • 原文地址:https://www.cnblogs.com/GadyPu/p/4488875.html
Copyright © 2011-2022 走看看