码迷,mamicode.com
首页 > 其他好文 > 详细

数据结构之红黑树

时间:2015-05-16 09:15:36      阅读:105      评论:0      收藏:0      [点我收藏+]

标签:

数据结构之红黑树
  红黑树(Red Black Tree) 是一种自平衡二叉查找树
红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,
从而获得较高的查找性能。
它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的:
它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。(度娘)
C++ stl里面的set,map底层就是用红黑树实现的。
红黑树具体的插入删除原理请参考<<算法导论>> 维基上面也讲得不错。
反正插入过程就是要解决”红-红”冲突,删除就是要解决”黑-黑”冲突。
下面是按照理我解的方式来实现红黑树,我只写了插入和删除过程,
其它的操作和普通的二叉树没多大区别。。
分享一个可以演示红黑树各种操作的网页:
https://www.cs.usfca.edu/~galles/visualization/RedBlack.html

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<ctime>
const int RED= 1;
const int BLACK = 0;
struct Node {
    int data;
    bool color;
    Node *fa, *left, *right;
    Node() :color(BLACK), data(0){ fa = left = right = NULL; }
    Node(int _v, Node *p) :data(_v), color(RED){ fa = left = right = p; }
};
struct RedBlackTree{
    Node *root, *null;
    void init(){
        null = new Node();
        root = null;
    }
    /*
    * 
    *    
    * 
    *    对红黑树的节点(x)进行右旋转
    *    
    *          px                            px
    *          /                             /
    *  ->    x                             y
    *       / \                           /     *  ->  y   rx     --(右旋)-->        ly   x
    *      / \                               /     *     ly  ry                            ry  rx
    *
    *
    *
    */
    //   旋转操作可以不传递引用(reference)话说传引用会快一些。。。
    void rotate_right(Node* &x) {
        Node *y = x->left;
        x->left = y->right;
        if (y->right != null) y->right->fa = x;
        y->fa = x->fa;
        if (x->fa == null) root = y;
        else if (x->fa->left == x) x->fa->left = y;
        else x->fa->right = y;
        y->right = x;
        x->fa = y;
    }
    //   左旋同上
    void rotate_left(Node* &x) {
        Node *y = x->right;
        x->right = y->left;
        if (y->left != null) y->left->fa = x;
        y->fa = x->fa;
        if (x->fa == null) root = y;
        else if (x->fa->left == x) x->fa->left = y;
        else x->fa->right = y;
        y->left = x;
        x->fa = y;
    }
    Node *find(Node *x, int &data) {
        while (x != null && x->data != data) {
            x = data < x->data ? x->left : x->right;
        }
        return x;
    }
    void insert(int v) {
        Node *x = root;
        Node *y = null;
        while (x != null) {        // 先找到带插入节点的位置,y保存该路径上最后一个节点
            y = x;
            x = v < x->data ? x->left : x->right;
        }
        x = new Node(v, null);
        if (y != null) {           // 确定新节点x与其父节点y的大小关系,再将它们连起来
            if (v < y->data) y->left = x;
            else y->right = x;
        } else {
            root = x;
        }
        x->fa = y;
        insert_fix(x);
    }
    void insert_fix(Node* &x) {
        while (x->fa->color != BLACK) {
            //    x为当前节点
            //    par x的父节点,Gp x的祖父节点, uncle x的叔叔节点
            Node *par = x->fa, *Gp = par->fa, *uncle = null;
            if (par == Gp->left) {                // 若父节点是祖父的左孩子
                uncle = Gp->right;                // 叔叔为祖父的右孩子
                if (uncle->color == RED) {        // 若叔叔颜色为红色 
            //    Case1:父亲和叔叔节点变为红色,祖父颜色变为红色
            //    将祖父节点设置为当前节点,再继续(因为Gp由黑变成红色,Gp的父节点可能为红色)
            //    所以要继续向上递归调整
                    par->color = BLACK;            
                    Gp->color = RED;
                    uncle->color = BLACK;
                    x = Gp;
                } else if (x == par->right) {
            //    Case2:x为右孩子,将父节点设置为当前节点,再进行左旋
            //    即变成Case3:
                    rotate_left(x = par);
                } else {
            //    Case3:x,父节点,祖父节点三者共线(左侧)
            //    将祖父节点改为红色,父节点改为黑色
            //    此时调整完成,红黑树性质得到恢复
                    Gp->color = RED;
                    par->color = BLACK;
                    rotate_right(Gp);
                }
            } else {
            //    同上,只是由左变成右而已
                uncle = Gp->left;
                if (uncle->color == RED) {
                    par->color = BLACK;
                    Gp->color = RED;
                    uncle->color = BLACK;
                    x = Gp;
                } else if (x == par->left) {
                    rotate_right(x = par);
                } else {
                    Gp->color = RED;
                    par->color = BLACK;
                    rotate_left(Gp);
                }
            }
        }
        //    将根置为黑色
        root->color = BLACK;
    }
    void del(int data){
        //    先找到待删除的节点
        Node *z = find(root, data);
        //    若没找到直接退出
        if (z == null) return;
        Node *y = z, *x = null;
        //    若z的左右孩子均不为空,则用y保存z的右子树中最小的节点
        if (z->left != null && z->right != null) {
            y= z->right;
            while (y->left != null) y = y->left;
        }
        //    此时y只有左子树,或只有右子树,或左右子树均不存在
        //    x可能为 null !!!
        x = y->left != null ? y->left : y->right;
        //    将x与y的父节点连接起来,这里可能会改变null节点的fa指针,但没有关系的。。。
        x->fa = y->fa;
        if (y->fa == null) root = x;
        //    确定y节点与其父亲的左右关系
        else  if (y->fa->left == y) y->fa->left = x;
        else y->fa->right = x;
        //    若z不等于y 拷贝数据,y才是真正要删除的节点
        if (z != y) z->data = y->data;
        //    如果删除的节点y的颜色为黑色,对树进行调整使得树满足红黑树的要求
        if (y->color == BLACK) del_fix(x);
        //    释放y
#ifdef DE_BUG
        delete y;
#endif
    }
    void del_fix(Node* &x) {
        while (x != root && x->color == BLACK) {
            //    x为当前节点
            //    par x的父节点, sibling x的兄弟节点
            Node *par = x->fa, *sibling = null;
            //    x为父节点的左孩子
            if (par->left == x){
            //    兄弟节点则为父节点的右孩子
                sibling = par->right;
            //    若兄弟节点的颜色为红色
                if (sibling->color == RED) {
            //    Case1:将兄弟节点染成黑色,父节点染成红色
                    sibling->color = BLACK;
                    par->color = RED;
            //    父节点左旋
                    rotate_left(par);
            //    重新设置兄弟节点
                    sibling = par->right;
                } else 
            //    Case2:兄弟节点为黑色,兄弟节点的左右孩子均为黑色
                if (sibling->left->color == BLACK && sibling->right->color == BLACK) {
            //    将兄弟节点染成红色
                    sibling->color = RED;
            //    将父节点变成当前节点
                    x = par;
                } else {
            //    Case3:兄弟节点为黑色,兄弟节点的右孩子为黑色,左孩子为红色
                    if (sibling->right->color == BLACK) {
            //    将兄弟节点的左孩子染成黑色
                        sibling->left->color = BLACK;
            //    将兄弟节点染成红色
                        sibling->color = RED;
            //    将兄弟节点右旋
                        rotate_right(sibling);
            //    重新设置兄弟节点
                        sibling = par->right;
                    }
            //    Case4:兄弟节点为黑色,兄弟节点的右孩子为红色,左孩子为任意颜色
            //    将兄弟节点染成父节点的颜色
                    sibling->color = par->color;
            //    父节点染成黑色
                    par->color = BLACK;
            //    兄弟节点的右孩子染成黑色
                    sibling->right->color = BLACK;
            //    将父节点左旋,跳出循环。
                    rotate_left(par);
                    break;
                }
            } else {
            //    同上,只是由左变成右而已
                sibling = par->left;
                if (sibling->color == RED) {
                    sibling->color = BLACK;
                    par->color = RED;
                    rotate_right(par);
                    sibling = par->left;
                } else 
                if (sibling->left->color == BLACK && sibling->right->color == BLACK) {
                    sibling->color = RED;
                    x = par;
                } else {
                    if (sibling->left->color == BLACK){
                        sibling->right->color = BLACK;
                        sibling->color = RED;
                        rotate_left(sibling);
                        sibling = par->left;
                    } else {
                        sibling->color = par->color;
                        par->color = BLACK;
                        sibling->left->color = BLACK;
                        rotate_right(par);
                        break;
                    }
                }
            }
        }
        x->color = BLACK;
    }
}rbt_tree;
int main() {
//  以下为测试
    int a = clock();
    rbt_tree.init();
    for (int i = 0; i < 2000000; i++) {
        rbt_tree.insert(i);
    }
#ifdef DE_BUG
    for (int i = 0; i < 2000000; i++) {
        rbt_tree.del(i);
    }
#endif
    rbt_tree.del(4);
    printf("%d\n", clock() - a);
    return 0;
}

下面是以bzoj3224/Tyvj 1728普通平衡树 为例实现了红黑树的各种操作
增加了size域(子树的大小),cnt域(关键字出现的次数)。
不想吐槽了,wa的快吐了%>_<% 由于插入,删除均用非递归方式实现的,
导致维护size,cnt域太麻烦了。。。
自己对拍了数据有输出1w多行才出错的(这咋调试啊 (>﹏<) )。还好最后发现了bug。
程序只通过了oj的数据,不知道还有没有隐含的bug →_→ 写的很搓懒得注释了。。。
欢迎各位游客批评指正O(∩_∩)O~~

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<ctime>
const int Max_N = 110000;
struct Node {
    int data, s, c;
    bool color;
    Node *fa, *ch[2];
    inline void set(int _v, bool _color, int i, Node *p) {
        data = _v, color = _color, s = c = i;
        fa = ch[0] = ch[1] = p;
    }
    inline void push_up() {
        s = ch[0]->s + ch[1]->s + c;
    }
    inline void push_down() {
        for (Node *x = this; x->s; x = x->fa) x->s--;
    }
    inline int cmp(int v) const {
        return data == v ? -1 : v > data;
    }
};
struct RedBlackTree {
    int top;
    Node *root, *null;
    Node stack[Max_N], *tail, *store[Max_N];
    void init() {
        tail = &stack[0];
        null = tail++;
        null->set(0, 0, 0, NULL);
        root = null;
        top = 0;
    }
    inline Node *newNode(int v) {
        Node *p = null;
        if (!top) p = tail++;
        else p = store[--top];
        p->set(v, 1, 1, null);
        return p;
    }
    inline void rotate(Node* &x, bool d ) {
        Node *y = x->ch[!d];
        x->ch[!d] = y->ch[d];
        if (y->ch[d]->s) y->ch[d]->fa = x;
        y->fa = x->fa;
        if (!x->fa->s) root = y;
        else x->fa->ch[x->fa->ch[0] != x] = y;
        y->ch[d] = x;
        x->fa = y;
        y->s = x->s;
        x->push_up();
    }
    inline void insert(int v) {
        Node *x = root, *y = null;
        while (x->s) {
            x->s++, y = x;    
            int d = x->cmp(v);
            if (-1 == d) {
                x->c++;
                return;
            }
            x = x->ch[d];
        }
        x = newNode(v);
        if (y->s) y->ch[v > y->data] = x;
        else root = x;
        x->fa = y;
        insert_fix(x);
    }
    inline void insert_fix(Node* &x) {
        while (x->fa->color) {
            Node *par = x->fa, *Gp = par->fa;
            bool d = par == Gp->ch[0];
            Node *uncle = Gp->ch[d];
            if (uncle->color) {
                par->color = uncle->color = 0;
                Gp->color = 1;
                x = Gp;
            } else if (x == par->ch[d]) {
                rotate(x = par, !d);
            } else {
                Gp->color = 1;
                par->color = 0;
                rotate(Gp, d);
            }
        }
        root->color = 0;
    }
    inline Node *find(Node *x, int data) {
        while (x->s && x->data != data) x = x->ch[x->data < data];
        return x;
    }
    inline void del_fix(Node* &x) {
        while (x != root && !x->color) {
            bool d = x == x->fa->ch[0];
            Node *par = x->fa, *sibling = par->ch[d];
            if (sibling->color) {
                sibling->color = 0;
                par->color = 1;
                rotate(x->fa, !d);
                sibling = par->ch[d];
            } else if (!sibling->ch[0]->color && !sibling->ch[1]->color) {
                sibling->color = 1, x = par;
            } else {
                if (!sibling->ch[d]->color) {
                    sibling->ch[!d]->color = 0;
                    sibling->color = 1;
                    rotate(sibling, d);
                    sibling = par->ch[d];
                }
                sibling->color = par->color;
                sibling->ch[d]->color = par->color = 0;
                rotate(par, !d);
                break;
            }
        }
        x->color = 0;
    }
    inline void del(int data) {
        Node *z = find(root, data);
        if (!z->s) return;
        if (z->c > 1) {        
            z->c--;
            z->push_down();
            return;
        }
        Node *y = z, *x = null;
        if (z->ch[0]->s && z->ch[1]->s) {
            y = z->ch[1];
            while (y->ch[0]->s) y = y->ch[0];
        }
        x = y->ch[!y->ch[0]->s];
        x->fa = y->fa;
        if (!y->fa->s) root = x;
        else y->fa->ch[y->fa->ch[1] == y] = x;
        if (z != y) z->data = y->data, z->c = y->c;
        y->fa->push_down();
        for (Node *k = y->fa; y->c > 1 && k->s && k != z; k = k->fa) k->s -= y->c - 1;
        if (!y->color) del_fix(x);
        store[top++] = y;
    }
    inline void kth(int k) {
        int t;
        Node *x = root;
        for (; x->s;) {
            t = x->ch[0]->s;
            if (k <= t) x = x->ch[0];
            else if (t + 1 <= k && k <= t + x->c) break;
            else k -= t + x->c, x = x->ch[1];
        }
        printf("%d\n", x->data);
    }
    inline void rank(int v) {
        int t, cur = 0;
        Node *x = root;
        for (; x->s;) {
            t = x->ch[0]->s;
            if (v == x->data) break;
            else if (v < x->data) x = x->ch[0];
            else cur += t + x->c, x = x->ch[1];
        }
        printf("%d\n", cur + t + 1);
    }
    inline void succ(int v) {
        int ret = 0;
        Node *x = root;
        while (x->s) {
            if (x->data > v) ret = x->data, x = x->ch[0];
            else x = x->ch[1];
        }
        printf("%d\n", ret);
    }
    inline void pred(int v) {
        int ret = 0;
        Node *x = root;
        while (x->s) {
            if (x->data < v) ret = x->data, x = x->ch[1];
            else x = x->ch[0];
        }
        printf("%d\n", ret);
    }
}rbt;
int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w+", stdout);
#endif
    rbt.init();
    int n, op, v;
    scanf("%d", &n);
    while (n--) {
        scanf("%d %d", &op, &v);
        if (1 == op) rbt.insert(v);
        else if (2 == op) rbt.del(v);
        else if (3 == op) rbt.rank(v);
        else if (4 == op) rbt.kth(v);
        else if (5 == op) rbt.pred(v);
        else rbt.succ(v);
    }
    return 0;
}

数据结构之红黑树

标签:

原文地址:http://blog.csdn.net/u012077152/article/details/45749933

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!