【算法笔记】伸展树 Splay Tree

资料

基本入门的就去看看:
《The Magical Splay》、《伸展树的基本操作与应用》、《运用伸展树解决数列维护问题》吧
参考code:
top-down和down-top两种,貌似竞赛里面都写自底向上的,可能是好维护size把。。
我用的是CLJ风格的。。。(他的代码太精炼了。。

struct node {
        node *ch[2], *p; // 左右子节点,父亲节点
        int key, sz; // sz 子树中节点数
        node (int x = 0, node *c = NULL):p(0), key(x), sz(1){ch[0] = ch[1] = c;}
        void sc( node * c, int d ) { // set child
            ch[d] = c;c->p  = this;
        }
        int which(node *x) {return ch[1] == x;} // x是否右节点
};

class splay_tree {
private:
    void upd( node *x ) { // 维护size
        x->sz = x->ch[0]->sz + x->ch[1]->sz + 1;
    }

    void rot( node *x ) {
        node *y = x->p;
        int d = y->which(x);
        y->sc(x->ch[!d], d);
        y->p->sc(x, y->p->which(y));
        x->sc(y, !d);
        upd(y);upd(x);
    }

    void splay( node *x, node *y ) {
        while ( x->p != y ) {
            if (x->p->p == y) rot(x);
            else {
                if (x->p->p->which(x->p) == x->p->which(x))
                    rot(x->p), rot(x);
                else
                    rot(x), rot(x);
            }
        }
    }

    node* find(int key) {
        node *z = root->ch[1];
        while (z != null) {
            if (z->key == key) return z;
            z = z->ch[key >= z->key];
        }
        return z;
    }

    // select kth: k is 0-based
    node* select(node* x, int k) {
        int r;
        for(;;) {
            r = x->ch[0]->sz;
            if (r == k) return x;
            x = x->ch[k>r];
            if (k>r) k -= r+1;
        }
    }

    node* subtree_min(node *x) {
        while (x->ch[0] != null) x = x->ch[0];return x;
    }

    node* subtree_max(node *x) {
        while (x->ch[1] != null) x = x->ch[1];return x;
    }

public:
    void insert(int key) {
        node *z = root->ch[1], *p = root;
        while (z != null) {
            p = z;z = z->ch[key >= z->key];
        }
        z = new node(key, null);
        p->sc(z, key >= p->key);
        splay(z, root);
    }

    // 先找到要删除的节点,旋至根
    // 如果左儿子或右儿子为空,直接删除
    // 找出后继,旋至右儿子处,然后替换x
    void erase(int key) {
        node *x = find(key);
        if (x == null) return;
        splay(x, root);
        if ( x->ch[0] == null ) root->sc(x->ch[1], 1);
        else if ( x->ch[1] == null ) root->sc(x->ch[0], 1);
        else {
            node *succ = subtree_min(x->ch[1]);
            if (succ->p != x) splay(succ, x);
            root->sc(succ, 1);
            succ->sc(x->ch[0], 0);
        }
        delete x;
        upd(root->ch[1]);
    }

    int select(int k) {
        if (root->ch[1]->sz < k) return back();
        node * x = select(root->ch[1], k);
        return x->key;
    }

    int front() {return subtree_min(root->ch[1])->key;}
    int back() {return subtree_max(root->ch[1])->key;}
    int count(int key) {return find(key) != null;}
    int _size() {
        if (root) {return root->ch[1]->sz;}
        return 0;
    }

public:
    splay_tree() {
        null = new node();null->sz = 0;
        root = new node(-inf, null);
    }

    ~splay_tree() {destroy(root->ch[1]);delete root;delete null;}

    void destroy(node *x) {
        if (x == null) return;
        destroy(x->ch[0]);destroy(x->ch[1]);
        delete x;
    }
public:
    node *null; // 空节点,代替NULL
    node *root; // root是一个值为-inf的空节点,splay的根在root->ch[1]
};

splay_tree * spt;

// 测试
void test() {
    rep(k, 1, 1000) {
        srand(time(0));
        spt = new splay_tree;
        multiset<int> s;
        int n = 1007;
        rep(i, 1, n) {
            int x = rand()%10007;
            spt->insert(x);s.insert(x);
            //cout << "insert: " << x << endl;
            assert(spt->front() == *s.begin() && spt->back() == *s.rbegin());
        }
        //cout << "test " << k << ": insert ok" << endl;
        rep(i, 1, n/2) {
            int x = rand()%10007;
            int ok1 = !!s.count(x);
            int ok2 = spt->count(x);
            assert(ok1 == ok2);
            if (ok1) {
                s.erase(s.find(x));spt->erase(x);
            }
            int d1 = s.size(), d2 = spt->_size();
            //cout << d1 << ' ' << d2 << endl;
            assert(s.size() == (spt->_size()) );
            //cout << "size ok" << endl;
            assert(spt->front() == *s.begin() && spt->back() == *s.rbegin());
        }
        cout << "find and erase " << k << " ok" << endl;
        delete spt;
        if (k == 2) {
            int stop = 1;
        }
    }

    cout << "test over\n" << endl;
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("input.in", "r", stdin);
#endif
    test();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值