zoukankan      html  css  js  c++  java
  • Splay学习笔记

    前面的话

    HZ的人好像都学了Splay,讲题的人也经常说Splay,我校众神也都会Splay。

    每次他们说Splay的时候我都说我不会太尴尬了,怒学Splay,学完之后真的感觉这是一种非常优美的数据结构。

    概述

    前置芝士:二叉搜索树

    二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
    若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;左、右子树也分别为二叉排序树
    同样的序列,因为排序不同,可能会生成不同的二叉排序树,查找效率性对就不一定了。如果是二叉排序树退化成一条链,效率就很低。
    (以上是抄的)

    注意到最后一句,于是就出现了一堆平衡树,用来提高BST的效率,避免BST退化成链的问题。

    GMK跟我说Treap没用,我就先学了Splay(

    各种操作

    Splay是一种优化过的BST,它通过伸展操作自我调整,提升效率。

    首先得看一个旋转操作,它是伸展操作的实现基础。

    旋转

    你看这个树它吼不吼哇 GG_BST

    图是盗的

    不吼哇!x那边太长啦,效率太低。于是我们把x转到y,重构树的结构,就能使树的深度更优。

    如何旋转能不改变BST性质?

    首先,y > x,所以y应该安置到x的右子树。然后把x连到z。y缺了个左儿子(它原来是有的),x多了个之前的右儿子,且这个右儿子必定小于y,所以把x的右儿子接成y的左儿子。

    GOOD_BST

    这是这个图的情况,总共有4种,没有人会闲的去写4个函数,要找普遍规律:x原来是y的哪个子树,x的这个子树就不会变,因为y会换掉另外一个子树。

    To be continued...

    模板:[LOJ #104. 普通平衡树] (https://loj.ac/problem/104)

    #include <bits/stdc++.h>
    
    const int N = 3e5 + 233, INF = 0x3f3f3f3f;
    int n;
    
    struct Splay {
        int root, tot, ch[N][2], siz[N], cnt[N], fa[N], val[N];
        Splay() {
            root = 1, ch[1][1] = 2;
            val[1] = -INF, val[2] = INF;
            cnt[1] = cnt[2] = 1;
            siz[1] = siz[2] = 1;
            fa[2] = 1, tot = 2;
        }
        inline int get(int x) { return ch[fa[x]][1] == x; }
        void update(int x) { siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + cnt[x]; }
        void rotate(int x) {
            int f1 = fa[x], f2 = fa[f1], id = get(x);
            ch[f1][id] = ch[x][id ^ 1], fa[ch[f1][id]] = f1;
            ch[f2][get(f1)] = x, fa[x] = f2;
            ch[x][id ^ 1] = f1, fa[f1] = x;
            update(f1);
        }
        void splay(int x, int goal = 0) {
            while (fa[x] != goal) {
                int f1 = fa[x], f2 = fa[f1];
                if (f2 != goal) {
                    if (get(x) == get(f1))
                        rotate(f1);
                    else
                        rotate(x);
                }
                rotate(x);
            }
            update(x);
            if (!goal)
                root = x;
        }
        void find(int x) {
            int p = root;
            while (ch[p][x > val[p]] && x != val[p]) p = ch[p][x > val[p]];
            splay(p);
        }
        void insert(int x) {
            int cur = root, p = 0;
            while (cur && val[cur] != x) p = cur, cur = ch[cur][x > val[cur]];
            if (cur)
                ++cnt[cur];
            else {
                cur = ++tot;
                if (p)
                    ch[p][x > val[p]] = cur;
                ch[cur][0] = ch[cur][1] = 0;
                val[cur] = x, fa[cur] = p;
                cnt[cur] = siz[cur] = 1;
            }
            splay(cur);
        }
        int get_pre(int x) {
            find(x);
            if (val[root] < x)
                return root;
            int p = ch[root][0];
            while (ch[p][1]) p = ch[p][1];
            return p;
        }
        int get_nxt(int x) {
            find(x);
            if (val[root] > x)
                return root;
            int p = ch[root][1];
            while (ch[p][0]) p = ch[p][0];
            return p;
        }
        void remove(int x) {
            int pre = get_pre(x), nxt = get_nxt(x);
            splay(pre), splay(nxt, pre);
            int del = ch[nxt][0];
            if (cnt[del] > 1)
                --cnt[del], splay(del);
            else
                ch[nxt][0] = 0;
        }
        int get_kth(int k) {
            int p = root;
            while (1) {
                if (ch[p][0] && k <= siz[ch[p][0]])
                    p = ch[p][0];
                else if (k > siz[ch[p][0]] + cnt[p])
                    k -= siz[ch[p][0]] + cnt[p], p = ch[p][1];
                else
                    return p;
            }
            return 0;
        }
    } splay;
    
    signed main() {
        scanf("%d", &n);
        for (int i = 1, opt, x; i <= n; i++) {
            scanf("%d%d", &opt, &x);
            switch (opt) {
                case 1:
                    splay.insert(x);
                    break;
                case 2:
                    splay.remove(x);
                    break;
                case 3:
                    splay.find(x);
                    printf("%d
    ", splay.siz[splay.ch[splay.root][0]]);
                    break;
                case 4:
                    printf("%d
    ", splay.val[splay.get_kth(x + 1)]);
                    break;
                case 5:
                    printf("%d
    ", splay.val[splay.get_pre(x)]);
                    break;
                case 6:
                    printf("%d
    ", splay.val[splay.get_nxt(x)]);
                    break;
            }
        }
        return 0;
    }
    
  • 相关阅读:
    南京师范大学2021年高等代数考研试卷
    有限阶全图边图两种颜色后同色三角形数量最少为?(2019年清华大学丘成桐数学英才班)
    关于三个变元的正整数解(2019年清华大学丘成桐数学英才班)
    [Oracle工程师手记]归档日志产生量太大时的简易分析手段
    [Oracle 工程师手记] 如何查看 FRA 的使用率
    [Oracle数据库工程师手记] Data Guard broker 与 ORA-32701
    [Oracle工程师手记]CRSD 进程与 password 文件
    [oracle 工程师手记]RMAN duplicate 发生ORA-19504、ORA-17502、ORA-15001、ORA-27140 错误的解决过程
    [Oracle工程师手记] 备份恢复双城记(三)
    [Oracle工程师手记] 备份恢复双城记(二)
  • 原文地址:https://www.cnblogs.com/gekoo/p/11272950.html
Copyright © 2011-2022 走看看