非旋转Treap

非旋转Treap

通过节点的优先级来维护树的平衡, 下面是普通非旋转Treap (弱平衡,

性质
  1. Treap是笛卡尔树的一种,只是 节点优先级是随机的
  2. T r e e + H e a p Tree+Heap Tree+Heap: 二叉搜索树+堆的性质
  3. 2个核心操作 分裂+合并
分裂 split

按照 节点权值分裂或者值的排名分裂

  1. s p l i t _ v a l split\_val split_val

    将一颗treap分裂成2个treap,第一个treap所有节点的权值 ≤ k e y \le key key , 第二个treap所有节点的权值 > k e y \gt key >key


    判断节点权值 v a l [ i n d e x ] val[index] val[index] 是否 ≤ k e y \le key key,

    1. v a l [ i n d e x ] ≤ k e y val[index]\le key val[index]key,则第一个 t r e a p treap treap就是 i n d e x index index及其左子树, 但右子树可能还有节点权值 ≤ k e y \le key key,所以再去 i n d e x index index的右子树去分裂
    2. v a l [ i n d e x ] > k e y val[index] \gt key val[index]>key,则第二个 t r e a p treap treap就是 i n d e x index index及其右子树.但左子树可能还有节点权值 > k e y \gt key >key ,所以再去 i n d e x index index左子树去分裂
    // oi-wiki 上的指针版
    pair<node *, node *> split(node *u, int key) {
      if (u == nullptr) {
        return make_pair(nullptr, nullptr);
      }
      if (key < u->key) {
        pair<node *, node *> o = split(u->lch, key);
        u->lch = o.second;
        return make_pair(o.first, u);
      } else {
        pair<node *, node *> o = split(u->rch, key);
        u->rch = o.first;
        return make_pair(u, o.second);
      }
    }
    
  2. s p l i t _ k t h split\_kth split_kth

    s p l i t _ v a l split\_val split_val一样, 注意一下去了右子树减去前面所有排名

合并 merge

合并2个 t r e a p treap treap是有顺序的,子树 x x x的中序遍历3,5,7,子树 y y y的中序遍历1,4,8,那么合并 ( x , y ) (x,y) (x,y) t r e a p treap treap的中序遍历为3,5,7,1,4,8, y y y树代表的序列拼接到 x x x树代表的序列

为了保持平衡, 根据节点的优先级来合并


例如 m e r g e ( x , y ) merge(x,y) merge(x,y),判断节点的优先级 p r i o r i t y priority priority

  1. p r i o r i t y [ x ] < p r i o r i t y [ y ] priority[x] < priority[y] priority[x]<priority[y] ,那么 x x x作为根节点, y y y x x x的右子树去合并 (因为 y y y要接在 x x x序列后面,根据中序遍历的性质,所有要去 x x x的右子树)
  2. p r i o r i t y [ x ] ≥ p r i o r i t y [ y ] priority[x] \ge priority[y] priority[x]priority[y],那么 y y y作为根节点, x x x y y y的左子树去合并,(因为中序遍历的性质,去 y y y的左子树)

// oi-wiki 指针版
node *merge(node *u, node *v) {
  if (u == nullptr) {
    return v;
  }
  if (v == nullptr) {
    return u;
  }
  if (u->priority > v->priority) {
    u->rch = merge(u->rch, v);
    return u;
  } else {
    v->lch = merge(u, v->lch);
    return v;
  }
}
其他操作 (都是基于分裂和合并)
  1. 建树(我现在只会一个一个插入)
  2. i n s e r t ( v a l ) = s p l i t _ v a l + n e w _ n o d e + m e r g e insert(val) = split\_val + new\_node + merge insert(val)=split_val+new_node+merge
  3. d e l e t e ( v a l ) = s p l i t _ v a l + s p l i t _ k t h + m e r g e delete(val) = split\_val + split\_kth + merge delete(val)=split_val+split_kth+merge
  4. r a n k ( v a l ) = s p l i t _ v a l + m e r g e rank(val) = split\_val + merge rank(val)=split_val+merge
  5. k t h ( k ) = s p l i t _ k t h ∗ 2 + m e r g e kth(k) = split\_kth*2 + merge kth(k)=split_kth2+merge
  6. p r e ( v a l ) = s p l i t _ v a l + s p l i t _ k t h + m e r g e pre(val) = split\_val + split\_kth + merge pre(val)=split_val+split_kth+merge
  7. n x t ( v a l ) = s p l i t _ v a l + s p l i t _ k t h + m e r g e nxt(val) = split\_val + split\_kth +merge nxt(val)=split_val+split_kth+merge

Luogu 平衡树

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

int ch[maxn][2],size[maxn],val[maxn],priority[maxn];
int tot,root;

void maintain(int x) { size[x] = size[ch[x][0]] + size[ch[x][1]] + 1; }

void split_val(int index,int value,int &x,int &y) {
    if (!index) {
        x = y = 0;
        return ;
    }
    if (val[index] <= value) {
        x = index;
        split_val(ch[index][1],value,ch[index][1],y);
    } else {
        y = index;
        split_val(ch[index][0],value,x,ch[index][0]);
    }
    maintain(index);
}

void split_kth(int index,int k,int &x,int &y) {
    if (!index) {
        x = y = 0;
        return ;
    }
    if (k <= size[ch[index][0]]) {
        y = index;
        split_kth(ch[index][0],k,x,ch[index][0]);
    } else {
        x = index;
        split_kth(ch[index][1],k-1-size[ch[index][0]],ch[index][1],y);
    }
    maintain(index);
}

int merge(int x,int y) { // 合并x 和 节点,y接在x后面
    if (!x || !y) return x + y;
    if (priority[x] < priority[y]) {
        // x 作为根节点,要满足中序遍历则,去x的右子树
        ch[x][1] = merge(ch[x][1],y);
        // 回溯时维护大小
        maintain(x);
        return x;
    } else { // y 作为根节点,为了满足中序遍历,x去左子树
        ch[y][0] = merge(x,ch[y][0]);
        maintain(y);
        return y;
    }
}

int new_node(int value) {
    val[++tot] = value;
    size[tot] = 1;
    return tot; // 返回节点编号
}

// 初始化节点的优先级
void init() {
    srand(time(0));
    for (int i=0; i<maxn-1; ++i)
        priority[i] = rand();
}

int n,i,x,r1,r2;

int main() {
    init();
    scanf("%d",&n);
    while (n--) {
        scanf("%d%d",&i,&x);
        if (i == 1) {
            // 在恰当的分裂 合并新的点
            split_val(root,x,root,r1);
            root = merge(root,merge(new_node(x),r1));
        }
        if (i == 2) { // 删除一个x值
            split_val(root,x-1,root,r1);
            split_kth(r1,1,r2,r1);
            root = merge(root,r1);
        }
        if (i == 3) { // x值的排名
            split_val(root,x-1,root,r1);
            printf("%d\n",size[root]+1);
            root = merge(root,r1);
        }
        if (i == 4) { // 查询排名为x的值
            split_kth(root,x-1,root,r1);
            split_kth(r1,1,r1,r2);
            printf("%d\n",val[r1]);
            root = merge(root,merge(r1,r2));
        }
        if (i == 5) { // x 的前驱
            split_val(root,x-1,root,r1);
            split_kth(root,size[root]-1,root,r2);
            printf("%d\n",val[r2]);
            root = merge(root,merge(r2,r1)); // 合并的时候注意r2,r1的顺序
        }
        if (i == 6) { // x 的后继
            split_val(root,x,root,r1);
            split_kth(r1,1,r1,r2);
            printf("%d\n",val[r1]);
            root = merge(root,merge(r1,r2));
        }
    }
    return 0;
}

oi-wiki Treap

%%%% 大佬非旋Treap
杂: 1天+1早上

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值