P3369 【模板】普通平衡树 stl / treap / splay / AVL

在这里插入图片描述


STL

咳咳 过题专用

#include <bits/stdc++.h>
using namespace std;
int n, m, k;

vector<int> v;

int main() {
    ios::sync_with_stdio(0);

    cin >> n;
    for (int i = 1, op, x; i <= n; ++i) {
        cin >> op >> x;

        auto it = lower_bound(v.begin(), v.end(), x);

        switch (op) {
            case 1:
            //插入x数
                v.insert(it, x);
                break;

            case 2:
            //删除x数 若有多个相同的数 因只删除一个
                v.erase(it);
                break;

            case 3:
            //查询x数的排名 排名定义为比当前数小的数的个数+1
                cout << it - v.begin() + 1 << endl;
                break;

            case 4:
            //查询排名为x的数
                cout << v[x - 1] << endl;
                break;

            case 5:
            //求x的前驱 前驱定义为小于x 且最大的数
                cout << v[it - v.begin() - 1] << endl;
                break;

            case 6:
            //求x的后继 后继定义为大于x 且最小的数
                cout << *upper_bound(v.begin(), v.end(), x) << endl;
        }
    }
    return 0;
}

Splay

学习博客

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;

namespace Splay {

    int f[N];//父节点
    int ch[N][2];//俩儿子
    int key[N];//val 关键字
    int cnt[N];//记录和i节点相同val的个数
    int size[N];//包括i的子树的大小
    int sz;//整棵树的大小
    int root;

    inline int pre();
    inline void Delete(int x);
    inline int next();

    inline void clear(int x) {//清空 用于删除之后
        ch[x][0] = ch[x][1] = f[x] = cnt[x] = key[x] = size[x] = 0;
    }

    inline bool get(int x) {//判断当前节点是它父节点的左儿子还是右儿子
        return ch[f[x]][1] == x;
    }

    inline void update(int x) {//更新size 用于发生修改之后
        if (x) {
            size[x] = cnt[x];
            if (ch[x][0]) size[x] += size[ch[x][0]];
            if (ch[x][1]) size[x] += size[ch[x][1]];
        }
    }

    inline void rotate(int x) {
        int old = f[x];//x的父节点
        int oldf = f[old];//父节点的父节点
        int whichx = get(x);

        //假设x是old左儿子 现在要使旋转后 x是old父亲
        //旋转的时候 把x的右儿子给old作为左儿子
        ch[old][whichx] = ch[x][whichx ^ 1];
        f[ch[old][whichx]] = old;
        //这个时候x的新的右儿子就是old 其父亲就是原来父亲的父亲
        ch[x][whichx ^ 1] = old;
        f[old] = x;
        f[x] = oldf;

        //然后修改oldf的儿子信息 本来是old的地方现在是x了
        if (oldf)
            ch[oldf][ch[oldf][1] == old] = x;

        update(old);
        update(x);
    }

    inline void splay(int x) {
        for (int fa; (fa = f[x]); rotate(x)) {
            //fa先取f[x] 如果f[fa]存在的话 也就是三代关系 需要先旋转f[x]
            //如果不存在 只有两代 就先旋转x本身
            if (f[fa])
                rotate((get(x) == get(fa)) ? fa : x);
        }
        root = x;
    }

    inline void insert(int x) {//插入
        if (root == 0) {//空树
            sz++;
            ch[sz][0] = ch[sz][1] = f[sz] = 0;
            root = sz;
            size[sz] = cnt[sz] = 1;
            key[sz] = x;
            return;
        }

        int now = root;
        int fa = 0;
        while (1) {
            if (x == key[now]) {
                cnt[now]++;
                update(now);
                update(fa);
                splay(now);
                break;
            }

            fa = now;
            now = ch[now][key[now] < x];
            if (now == 0) {//创建新的节点
                sz++;
                ch[sz][0] = ch[sz][1] = 0;
                f[sz] = fa;
                size[sz] = cnt[sz] = 1;
                ch[fa][key[fa] < x] = sz;
                key[sz] = x;
                update(fa);
                splay(sz);
                break;
            }
        }
    }

    inline int value_rank(int x) {//查询v的排名
        int ans = 0;
        int now = root;
        while (1) {
            if (x < key[now]) {
                now = ch[now][0];
            } else {
                //如果在右子树里
                ans += (ch[now][0] ? size[ch[now][0]] : 0);//先把左子树的大小加上
                if (x == key[now]) {
                    splay(now);//先旋转 emmm不旋转会咋样?
                    return ans + 1;
                }
                ans += cnt[now];//再把中间的加上
                now = ch[now][1];//往右子树走
            }
        }
    }

    inline int rank_value(int x) {//找到排名为x的点
        int now = root;
        while (1) {
            if (ch[now][0] && x <= size[ch[now][0]])
                now = ch[now][0];
            else {
                int tmp = (ch[now][0] ? size[ch[now][0]] : 0) + cnt[now];
                if (x <= tmp) return key[now];
                x -= tmp;
                now = ch[now][1];
            }
        }
    }

//------------------------------------

    inline int Pre(int x) {//求前驱节点
        insert(x);
        int res = key[pre()];
        Delete(x);
        return res;
    }

    inline int pre() {
        int now = ch[root][0];
        while (ch[now][1]) now = ch[now][1];
        return now;
    }

//------------------------------------

    inline int Suf(int x) {//求后继节点
        insert(x);
        int res = key[next()];
        Delete(x);
        return res;
    }

    inline int next() {
        int now = ch[root][1];
        while (ch[now][0])now = ch[now][0];
        return now;
    }

    inline void Delete(int x) {
        int whatever = value_rank(x);
        if (cnt[root] > 1) {
            cnt[root]--;
            update(root);
            return;
        }

        //只有根节点 且根节点只有一个
        if (!ch[root][0] && !ch[root][1]) {
            clear(root);
            root = 0;
            return;
        }

        //只有一个孩子
        if (!ch[root][0]) {
            int oldroot = root;
            root = ch[root][1];
            f[root] = 0;
            clear(oldroot);
            return;
        } else if (!ch[root][1]) {
            int oldroot = root;
            root = ch[root][0];
            f[root] = 0;
            clear(oldroot);
            return;
        }

        //有两个儿子
        int leftbig = pre();
        int oldroot = root;
        splay(leftbig);
        ch[root][1] = ch[oldroot][1];
        f[ch[oldroot][1]] = root;
        clear(oldroot);
        update(root);
        return;
    }
}
using namespace Splay;//不能使用结构体封装 = = 爆栈了
//我就不明白了 treap也差不多大小 怎么就它能封装啊 吐血

int main() {
    ios::sync_with_stdio(0);

    cin >> n;
    for (int i = 1, op, x; i <= n; ++i) {
        cin >> op >> x;

        switch (op) {
            case 1:
            //插入x数
                insert(x);
                break;

            case 2:
            //删除x数 若有多个相同的数 因只删除一个
                Delete(x);
                break;

            case 3:
            //查询x数的排名 排名定义为比当前数小的数的个数+1
                cout << value_rank(x) << endl;
                break;

            case 4:
            //查询排名为x的数
                cout << rank_value(x) << endl;
                break;

            case 5:
            //求x的前驱 前驱定义为小于x 且最大的数
                cout << Pre(x) << endl;
                break;

            case 6:
            //求x的后继 后继定义为大于x 且最小的数
                cout << Suf(x) << endl;
        }
    }
    return 0;
}

Treap 自平衡二叉查找树

一个随机附加域满足堆的性质的二叉搜索树
学习来源 - 这位大佬学的还很详细,值得瞧瞧

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;

struct Treap {//Tree heap 树堆板子

    Treap() {
        init();
    }

    struct node {
        int l, r;//俩儿子
        int val;//节点的值 也叫关键码
        int data;//节点的优先级
        int num;//有多少个与当前节val相等的节点 包括自身
        int size;//节点有多少个后辈 包括自身
    } t[N];

    int tot;
    int root;//根节点

    int getNode(int val) {
        ++tot;
        t[tot] = {0, 0, val, rand(), 1, 1};//随机数分配优先级
        return tot;
    }

    void update(int p) {//求size 左子树的大小+右子树的大小+本人那一堆相同的兄弟
        t[p].size = t[t[p].l].size + t[t[p].r].size + t[p].num;
    }

    void build() {//建树
        getNode(INT_MIN); //插入负无穷的 - 根节点
        getNode(INT_MAX);//插入正无穷点 - 根节点的右节点
        //建这两个点 目的是为了防止平衡树退化成链
        root = 1;
        t[1].r = 2;
        update(root);
    }

    void RightRotate(int &p) {//右旋 旋转的同时修改了节点
        int q = t[p].l;
        t[p].l = t[q].r;
        t[q].r = p;
        p = q;
        update(t[p].r);//更新右孩子的Size
        update(p);
    }

    void LeftRotate(int &p) {//左旋 zag
        int q = t[p].r;
        t[p].r = t[q].l;
        t[q].l = p;
        p = q;
        update(t[p].l);//更新左孩子的Size
        update(p);
    }

    //----------------------------------

    void insert(int val) {
        INSERT(root, val);
    }

    void INSERT(int &p, int val) {
        if (p == 0) {//节点不存在 并已经找到其应有的位置
            p = getNode(val);
            return;
        }

        if (val == t[p].val) {//val值已经存在
            t[p].num++;
            update(p);
            return;
        }

        if (val < t[p].val) {//往左子树延伸
            INSERT(t[p].l, val);
            if (t[p].data < t[t[p].l].data) {//比较优先级
                RightRotate(p);
            }
        } else {//往右子树延伸
            INSERT(t[p].r, val);
            if (t[p].data < t[t[p].r].data) {
                LeftRotate(p);
            }
        }
        update(p);
    }

    //----------------------------------

    void Delete (int val){ //好难受啊 delete被用了
        DELETE(root,val);
    }

    void DELETE(int &p, int val) {
        if (p == 0)return;

        if (val == t[p].val) {
            if (t[p].num > 1) {
                t[p].num--;
                update(p);
                return;
            }

            if (t[p].l || t[p].r) {
                if (t[p].r == 0 || t[t[p].l].data > t[t[p].r].data) {
                    //如果右子树不存在 或者左子树的优先级大于右子树的
                    //右旋后再往右子树下延伸
                    RightRotate(p);
                    DELETE(t[p].r, val);
                } else {//否则就左旋 再往左子树下延伸
                    LeftRotate(p);
                    DELETE(t[p].l, val);
                }
                update(p);
            } else p = 0;
            return;
        }

        if (val < t[p].val) DELETE(t[p].l, val);
        else DELETE(t[p].r, val);
        update(p);
    }

    //----------------------------------

    int value_rank(int val){
        return Sort(root, val) - 1;
    }

    int Sort(int p, int val) {//查询val值的排序 由值得出排名
        if (p == 0)return 0;
        if (val == t[p].val) return t[t[p].l].size + 1;//左子树的个数+1
        if (val < t[p].val) return Sort(t[p].l, val);
        return Sort(t[p].r, val) + t[t[p].l].size + t[p].num;
    }

    //----------------------------------

    int rank_value(int k){//由排名得出值
        return Value(root, k + 1);
    }

    int Value(int p, int k) {//由排名得出值
        if (p == 0)return INT_MAX;//如果不存在 返回最大值
        if (t[t[p].l].size >= k)
            return Value(t[p].l, k);
        if (t[t[p].l].size + t[p].num >= k)//根节点
            return t[p].val;
        return Value(t[p].r, k - t[t[p].l].size - t[p].num);
    }

	 //----------------------------------

    int Pre(int val) {//求值得前驱节点的值 如果整棵树只有根节点 返回的是的就是root
        //记val的节点为p 求pre的值
        int pre = 1;
        int p = root;//从根节点开始往下找
        while (p) {
            if (val == t[p].val) {//如果等于当前节点
                if (t[p].l > 0) {//往左子树的右子树最右端找
                    p = t[p].l;
                    while (t[p].r > 0) p = t[p].r;
                    pre = p;
                }
                break;
            }

            //移动pre指针
            if (t[p].val < val && t[p].val > t[pre].val) pre = p;

            //根据val的大小 往下深入
            if (val < t[p].val) p = t[p].l;
            else p = t[p].r;
        }
        return t[pre].val;
    }

	 //----------------------------------

    int Suf(int val) {//求后继节点的值
        int suf = 2;
        int p = root;
        while (p) {
            if (val == t[p].val) {
                if (t[p].r > 0) {
                    p = t[p].r;
                    while (t[p].l > 0) {
                        p = t[p].l;
                    }
                    suf = p;
                }
                break;
            }
			//移动pre指针
            if (t[p].val > val && t[p].val < t[suf].val) suf = p;

            if (val < t[p].val) p = t[p].l;
            else p = t[p].r;
        }
        return t[suf].val;
    }

    void init() {
        tot = 0;
        build();
    }
    
};

int main() {
    ios::sync_with_stdio(0);

    Treap t;
    cin >> n;
    for (int i = 1, op, x; i <= n; ++i) {
        cin >> op >> x;

        switch (op) {
            case 1:
            //插入x数
                t.insert(x);
                break;

            case 2:
            //删除x数 若有多个相同的数 因只删除一个
                t.Delete(x);
                break;

            case 3:
            //查询x数的排名 排名定义为比当前数小的数的个数+1
                cout << t.value_rank(x) << endl;
                break;

            case 4:
            //查询排名为x的数
                cout << t.rank_value(x) << endl;
                break;

            case 5:
            //求x的前驱 前驱定义为小于x 且最大的数
                cout << t.Pre(x) << endl;
                break;

            case 6:
            //求x的后继 后继定义为大于x 且最小的数
                cout << t.Suf(x) << endl;
        }
    }
    return 0;
}

留一个坑给AVL 等大四了再来撸一撸板子

AVL


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值