POJ - 3481 Double Queue 【Splay 模板题】

传送门

最基本的Splay模板题,没有任何花里胡哨的操作,可以用于加深理解模板中每个函数。
Splay学习博客:
https://baijiahao.baidu.com/s?id=1613228134219334653&wfr=spider&for=pc
https://www.cnblogs.com/cjyyb/p/7499020.html               ///推荐当模板

代码:(有详细注释+样例)

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<vector>

using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1e6 + 5;
int father[maxn], child[maxn][2], size[maxn];
int cnt[maxn], root, times;
int value[maxn], sign[maxn];

bool check(int x) {     ///查询x是father[x]的左儿子还是右儿子
    return child[father[x]][1] == x;
}
void pushup(int x) {
    size[x] = size[child[x][0]] + size[child[x][1]] + cnt[x];
}

void rotate(int x) {        ///旋转操作,将x和father[x]换位置
    int y = father[x], z = father[y], k = check(x), w = child[x][k ^ 1];
    child[y][k] = w, father[w] = y;
    child[z][check(y)] = x, father[x] = z;
    child[x][k ^ 1] = y, father[y] = x;
    pushup(y), pushup(x);
}
void Splay(int x, int goal) {   ///将x节点旋转到goal的下方
    while (father[x] != goal) {
        int y = father[x], z = father[y];
        if (z != goal) {    ///存在爷爷节点
            if (check(x) == check(y))   ///如果在链的同一侧(为了保证树的平衡)
                rotate(y);
            else
                rotate(x);
        }
        rotate(x);
    }
    if (!goal)
        root = x;
}
void Insert(int x, int flag) {   ///插入操作
    int temp = root, fa = 0;
    while (temp && value[temp] != x) {
        fa = temp;
        temp = child[temp][x > value[temp]];
    }
    if (temp)
        cnt[temp]++;
    else {
        temp = ++times;
        if (fa) ///如果存在父亲(不存在父亲就是一棵空树,u就是当前的根)
            child[fa][x > value[fa]] = temp;
        child[temp][0] = child[temp][1] = 0;
        sign[temp] = flag;
        father[temp] = fa, value[temp] = x;
        cnt[temp] = size[temp] = 1;
    }
    Splay(temp, 0);
}
int find_k(int k) {         ///查询中序遍历的第k个的节点
    int temp = root;
    while (1) {
        //pushdown(temp);     ///标记下传
        if (child[temp][0] && k <= size[child[temp][0]]) {
            temp = child[temp][0];
        } else if (k > size[child[temp][0]] + cnt[temp]) {
            k -= (size[child[temp][0]] + cnt[temp]);    ///先计算数量后向下递归
            temp = child[temp][1];
        } else
            return temp;
    }
}
void find(int x) {      ///如果存在最大的小于等于的数所在的节点splay到根,不存在就是第一个大于x的节点
    int temp = root;
    while (child[temp][x > value[temp]] && value[temp] != x)
        temp = child[temp][x > value[temp]];
    Splay(temp, 0);
}
int Next(int x, int flag) {     ///flag=0,查找前驱    flag=1查找后继
    find(x);
    int temp = root;
    if (value[temp] < x && !flag)
        return temp;
    if (value[temp] > x && flag)
        return temp;
    temp = child[temp][flag];
    while (child[temp][flag ^ 1])
        temp = child[temp][flag ^ 1];
    return temp;
}
void Delete(int x) {        ///删除权值为x的节点
    int pre = Next(x, 0);
    int last = Next(x, 1);
    Splay(pre, 0), Splay(last, pre);
    int temp = child[last][0];
    if (cnt[temp] > 1) {    ///存在多个则cnt计数-1
        cnt[temp]--;
        Splay(temp, 0);
    } else                  ///删除该节点
        child[last][0] = 0;
}
int main() {
    int op, k, p, num = 2;          ///记录数中的节点个数
    Insert(-1, 0), Insert(INF, 0); ///方便删除节点
    while(scanf("%d", &op) != EOF, op) {
        if(op == 1) {
            scanf("%d %d", &k, &p);
            Insert(p, k);
            num++;
        } else if(op == 2) {
            if(num == 2) {
                printf("0\n");
                continue;
            }
            int x = find_k(num - 1);
            printf("%d\n", sign[x]);
            Delete(value[x]);
            num--;
        } else {
            if(num == 2) {
                printf("0\n");
                continue;
            }
            int x = find_k(2);
            printf("%d\n", sign[x]);
            Delete(value[x]);
            num--;
        }
    }
    return 0;
}
/*
    1 1 1
    1 2 2
    1 3 3
    1 4 4
    1 5 5
    1 6 6
    1 7 7
    1 8 8
    1 9 9
    2
    2
    2
    2
    2
    2
    2
    2
    2
    0
    Output:
    9
    8
    7
    6
    5
    4
    3
    2
    1
*/


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值