Splay树-模板

tarjan杀我
更新: 2020.11.17

//对于查找频率较高的节点,使其处于离根节点相对较近的节点

//我们定义一个结点与他父亲的关系是x,
//那么在旋转时,他的父亲成为了他的!x儿子,
//并且那个上文中所说的“多余结点”,
//同时也是当前节点的!x儿子,
//但在旋转之后需要成为当前节点的“前”父节点的x儿子

// 1.插入 x 数
// 2.删除 x 数(若有多个相同的数,则只删除一个)
// 3.查询 x 数的排名(排名定义为比当前数小的数的个数 +1 )
// 4.查询排名为 x 的数
// 5.求 x 的前驱(前驱定义为小于 x,且最大的数)
// 6.求 x 的后继(后继定义为大于 x,且最小的数)

#include <bits/stdc++.h>
#define ll long long
#define _for(i,a,b) for(ll i = (a);i < (b); ++i)
#define sc scanf
#define pr printf
#define TLE ios::sync_with_stdio(false); cin.tie(0);
const int maxn = 500100;
const int INF = 0x3f3f3f3f;
using namespace std;

#define fa(x) tree[x].fath
#define ls(x) tree[x].ch[0]
#define rs(x) tree[x].ch[1]

struct Node {
    int fath; //父节点
    int ch[2]; //子节点
    int cnt; //权值相同节点的数量
    int w; //权值
    int _size; //子树节点数量
}tree[maxn];
int root = 0; //保存当前根节点
int tot = 0; //保存不同权值的节点总数

void push_up(int u) { //计算儿子数
    tree[u]._size = tree[tree[u].ch[0]]._size + tree[tree[u].ch[1]]._size + tree[u].cnt;
}

//判断节点是否为Splay的根节点
inline bool IsRoot(int x) {
    return ls(fa(x)) != x && rs(fa(x)) != x;
    //1.若x与fa(x)之间的边是虚边,那么它的父亲的孩子中不会有他(不在同一个splay内)
    //2. splay的根节点与其父亲之间的边是虚边
}

#define id(x,y) tree[x].ch[1] == y

void rotate(int x) { //单旋
    int y = fa(x);
    int z = fa(y);
    int k = id(y,x); // k = 1 则x是y的右儿子
                     // k = 0 则x是y的左儿子
    //这里如果不判断y是否根节点,那么当y是根节点的时候,0节点的儿子就会被更新为x
    //这样x就永远不能被判断为根节点,也就会无限循环下去了
    //但是这里不更新x的父亲的话就会出现无限递归的情况
    if(!IsRoot(y))
        tree[z].ch[id(z,y)] = x; // y < z -> x < z;
    fa(x) = z;                   // y > z -> x < z;
    tree[y].ch[k] = tree[x].ch[k ^ 1]; // 如果当前节点是x的k孩子,旋转之后则必是y的!k孩子
    fa(tree[x].ch[k ^ 1]) = y;
    tree[x].ch[k ^ 1] = y; // 若x是y的左儿子,则y必是x的右儿子,反之同理
    fa(y) = x;
    push_up(y);
    push_up(x);
}

void Splay(int x, int goal) {
    while(fa(x) != goal) {
        int y = fa(x);
        if(fa(y) != goal)
            rotate( (ls(y) == x) ^ (ls(fa(y)) == y) ? x : y );
        rotate(x);
    }
    if(goal == 0) { //更新当前根节点
        root = x;
    }
}

inline void Find(int x) {
    int u = root;
    if(!u) return;
    while(tree[u].ch[x > tree[u].w] && x != tree[u].w) {
        u = tree[u].ch[x > tree[u].w];
    }
    Splay(u,0); //每次查找更新根节点
}

// 5.求 x 的前驱(前驱定义为小于 x,且最大的数)
// 6.求 x 的后继(后继定义为大于 x,且最小的数)
inline int Next(int x,int f) { //前驱 f = 0;
                               //后继 f = 1;
    Find(x);
    int u = root;
    if(tree[u].w > x && f) return u; //后继
    if(tree[u].w < x && !f) return u; //前驱
    u = tree[u].ch[f];
    while(tree[u].ch[f ^ 1]) {
        u = tree[u].ch[f ^ 1];
    }
    return u;
}

// 2.删除 x 数(若有多个相同的数,则只删除一个)
inline void Delete(int x) {
    int last = Next(x,0); //查找前驱
    int next = Next(x,1); //查找后继
    Splay(last,0);
    Splay(next,last);
    int del = tree[next].ch[0];
    if(tree[del].cnt > 1) {
        tree[del].cnt--;
        Splay(del,0);
    }
    else {
        tree[next].ch[0] = 0;
    }
    return;
}

// 1.插入 x 数
inline void Insert(int x) {
    int now = root, fa = 0;
    while(now && tree[now].w != x) {
        fa = now;
        now = tree[now].ch[x > tree[now].w];
    }
    if(now) { //存在权值为x的节点
        tree[now].cnt++;
    }
    else { //不存在权值为x的节点,创建新节点
        now = ++tot;
        if(fa) {
            tree[fa].ch[x > tree[fa].w] = now;
        }
        tree[tot].ch[0] = 0;
        tree[tot].ch[1] = 0;
        tree[tot].fath = fa;
        tree[tot].w = x;
        tree[tot].cnt = 1;
        tree[tot]._size = 1;
    }
    Splay(now,0);
}

// 4.查询排名为 x 的数
inline int K_th(int x) {
    int u = root;
    if(tree[u]._size < x) {
        return false;
    }
    while(1) {
        int y = tree[u].ch[0];
        if(x > tree[y]._size + tree[u].cnt) { //排名在u之后
            x -= tree[y]._size + tree[u].cnt;
            u = tree[u].ch[1];
        }
        else if(tree[y]._size >= x) {
            u = y;
        }
        else {
            return tree[u].w;
        }
    }
}

int main() {
    int T;
    sc("%d",&T);
    int opt,x;
    Insert(-2147483647);
    Insert(+2147483647);
    while(T--) {
        sc("%d",&opt);
        if(opt == 1) {
            sc("%d",&x);
            Insert(x);
        }
        else if(opt == 2) {
            sc("%d",&x);
            Delete(x);
        }
        else if(opt == 3) {
            sc("%d",&x);
            Find(x);
            pr("%d\n",tree[tree[root].ch[0]]._size);
        }
        else if(opt == 4) {
            sc("%d",&x);
            pr("%d\n",K_th(x+1));
        }
        else if(opt == 5) {
            sc("%d",&x);
            pr("%d\n",tree[Next(x,0)].w);
        }
        else if(opt == 6) {
            sc("%d",&x);
            pr("%d\n",tree[Next(x,1)].w);
        }
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值