Treap

Treap

1.算法分析

2.模板

#include <bits/stdc++.h>

using namespace std;

const int N = 100010, INF = 1e8;

int n;
struct Node {
    int l, r;
    int key, val;  // key是存储的值,val是heap分配的值,用来维护heap的性质
    int cnt,
        size;  // cnt记录当前key的值出现的次数,size记录以当前节点为根的子树的大小
} tr[N];

int root, idx;  // root为根,idx为节点个数

// 向上更新操作
void pushup(int p) {
    tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + tr[p].cnt;
}

// 产生新节点
int get_node(int key) {
    tr[++idx].key = key;
    tr[idx].val = rand();
    tr[idx].cnt = tr[idx].size = 1;
    return idx;
}

// 建立一颗treap
void build() {
    get_node(-INF), get_node(INF);
    root = 1, tr[1].r = 2;
    pushup(root);
}

void zig(int &p) {  // 右旋
    int q = tr[p].l;
    tr[p].l = tr[q].r, tr[q].r = p, p = q;
    pushup(tr[p].r), pushup(p);
}

void zag(int &p) {  // 左旋
    int q = tr[p].r;
    tr[p].r = tr[q].l, tr[q].l = p, p = q;
    pushup(tr[p].l), pushup(p);
}

// 插入
void insert(int &p, int key) {
    if (!p)
        p = get_node(key);  // 如果没有这个节点,那么生成一个
    else if (tr[p].key == key)
        tr[p].cnt++;  // 如果这个节点存在,cnt加一
    else if (tr[p].key > key) {  // 如果当前key值大于要插入的值,说明要在左子树插入
        insert(tr[p].l, key);
        if (tr[tr[p].l].val > tr[p].val) zig(p);
    } else {  // 说明要在右子树插入
        insert(tr[p].r, key);
        if (tr[tr[p].r].val > tr[p].val) zag(p);
    }
    pushup(p);  // 向上更新
}

// 删除
void remove(int &p, int key) {
    if (!p) return;
    if (tr[p].key == key) {  // 如果找到
        if (tr[p].cnt > 1)
            tr[p].cnt--;  // 如果这个key有多个节点,直接cnt--
        else if (tr[p].l || tr[p].r) {  // 如果只有一个节点且左右子树不为空
            if (!tr[p].r || tr[tr[p].l].val > tr[tr[p].r].val) {  // 右子树为空,或者左子树val大于右子树val
                zig(p);                // 右旋来使得满足heap性质
                remove(tr[p].r, key);  // 删除右子树
            } else {
                zag(p);
                remove(tr[p].l, key);
            }
        } else
            p = 0;  // 如果左右子树均为空,直接删除
    } else if (tr[p].key > key)
        remove(tr[p].l, key);  // 如果当前节点key大于要删除节点的key,去左子树进行删除
    else
        remove(tr[p].r, key);

    pushup(p);  // 更新
}

int get_rank_by_key(int p, int key) {  // 通过数值找排名
    if (!p) return 0;                  // 本题中不会发生此情况
    if (tr[p].key == key) return tr[tr[p].l].size + 1;
    if (tr[p].key > key) return get_rank_by_key(tr[p].l, key);
    return tr[tr[p].l].size + tr[p].cnt + get_rank_by_key(tr[p].r, key);
}

int get_key_by_rank(int p, int rank) {  // 通过排名找数值
    if (!p) return INF;                 // 本题中不会发生此情况
    if (tr[tr[p].l].size >= rank) return get_key_by_rank(tr[p].l, rank);
    if (tr[tr[p].l].size + tr[p].cnt >= rank) return tr[p].key;
    return get_key_by_rank(tr[p].r, rank - tr[tr[p].l].size - tr[p].cnt);
}

int get_prev(int p, int key) {  // 找到严格小于key的最大数
    if (!p) return -INF;
    if (tr[p].key >= key)
        return get_prev(tr[p].l, key);  // 如果找到严格小于等于key的最大数,那么这里应该改为tr[p].key
                   // > key
    return max(tr[p].key, get_prev(tr[p].r, key));
}

int get_next(int p, int key) {  // 找到严格大于key的最小数
    if (!p) return INF;
    if (tr[p].key <= key)
        return get_next(tr[p].r, key);  // 如果找到严格大于等于key的最大数,那么这里应该改为tr[p].key
                   // < key
    return min(tr[p].key, get_next(tr[p].l, key));
}

int main() {
    build();
    scanf("%d", &n);
    while (n--) {
        int opt, x;
        scanf("%d%d", &opt, &x);
        if (opt == 1)
            insert(root, x);
        else if (opt == 2)
            remove(root, x);
        else if (opt == 3)
            printf("%d\n", get_rank_by_key(root, x) - 1);
        else if (opt == 4)
            printf("%d\n", get_key_by_rank(root, x + 1));
        else if (opt == 5)
            printf("%d\n", get_prev(root, x));
        else
            printf("%d\n", get_next(root, x));
    }

    return 0;
}

3.典型例题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值