入门平衡树——Treap
前置芝士:
(其实
上回书说道,为了维护
的基本思路
满足
而这种既维持了
注意:不是单纯的转,而是可以通过这种方式形象地理解维护平衡的过程。
“单旋转”是最基本的旋转操作,它又分为“左旋”和“右旋”。如下图所示:
以右旋为例。在初始情况下,
“右旋”操作在维持
当
右旋也叫
代码:
void zig(int &p) {
int q = tr[p].ls;
tr[p].ls = tr[q].rs, tr[q].rs = p, p = q;
}
左旋也叫
void zag(int &p) {
int q = tr[p].rs;
tr[p].rs = tr[q].ls, tr[q].ls = p, p = q;
}
经过一系列合理的旋转,就可以使
那么,什么才是“合理”的旋转操作呢?上篇文章提到了:在随机数据下,普通的
顺便贴上大佬总结的绕口令口诀:
左旋拎右左挂右,右旋拎左右挂左——AgOH
插入
基本思路和朴素的
void insert(int &p, int key) {
if(p == 0) p = New(key);
else if(tr[p].key == key) tr[p].cnt++;
else if(key < tr[p].key) {
insert(tr[p].ls, key);
if(tr[tr[p].ls].val > tr[p].val) zig(p); //左大右旋
}
else {
insert(tr[p].rs, key);
if(tr[tr[p].rs].val > tr[p].val) zag(p); //右大左旋
}
}
删除
删除其实是一个删繁就简的过程:先检索到需要删除的节点,然后不断把它旋转成为叶结点,然后直接删掉就行了。这样就可以避免朴素
代码:
void remove(int &p, int key) {
if(!p) return ;
if(key == tr[p].key) {
if(tr[p].cnt > 1) tr[p].cnt--;
else if(tr[p].ls || tr[p].rs) {
if(tr[tr[p].ls].val > tr[tr[p].rs].val && !tr[p].rs) {
zig(p); //左大右旋
remove(tr[p].rs, key);
}
else {
zag(p); //右大左旋
remove(tr[p].ls, key);
}
}
else p = 0;
}
else if(key < tr[p].key) remove(tr[p].ls, key);
else remove(tr[p].rs, key);
}
求前驱/后继
和朴素的
例题 P3369 【模板】普通平衡树
在这道例题中由于有根据权值求排名和根据排名求权值两个操作,所以多维护几个值:
同时还要新加入一个
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
struct Treap {
int ls, rs; //左右儿子的下标
int key, val; //key 代表权值,val 是随机赋的值
int cnt, siz;
}tr[N];
int n, root, idx, inf = 0x7ffffff;
int New(int key) {
tr[++idx].key = key;
tr[idx].val = rand();
tr[idx].cnt = tr[idx].siz = 1;
return idx;
}
inline void pushup(int p) {tr[p].siz = tr[tr[p].ls].siz + tr[tr[p].rs].siz + tr[p].cnt;}
void zig(int &p) {
int q = tr[p].ls;
tr[p].ls = tr[q].rs, tr[q].rs = p, p = q;
pushup(tr[p].rs), pushup(p); //从下往上更新
}
void zag(int &p) {
int q = tr[p].rs;
tr[p].rs = tr[q].ls, tr[q].ls = p, p = q;
pushup(tr[p].ls), pushup(p); //从下往上更新
}
void build() {
New(-inf);
New(inf);
root = 1;
tr[1].rs = 2;
pushup(root);
if(tr[1].val < tr[2].val) zag(root); //由于初始节点赋的 val 可能不满足堆性质,所以也要旋一下
}
void insert(int &p, int key) {
if(p == 0) p = New(key);
else if(tr[p].key == key) tr[p].cnt++;
else if(key < tr[p].key) {
insert(tr[p].ls, key);
if(tr[tr[p].ls].val > tr[p].val) zig(p);
}
else {
insert(tr[p].rs, key);
if(tr[tr[p].rs].val > tr[p].val) zag(p);
}
pushup(p); //每次操作完都要进行更新
}
void remove(int &p, int key) {
if(!p) return ;
if(key == tr[p].key) {
if(tr[p].cnt > 1) tr[p].cnt--;
else if(tr[p].ls || tr[p].rs) {
if(tr[tr[p].ls].val > tr[tr[p].rs].val && !tr[p].rs) {
zig(p);
remove(tr[p].rs, key);
}
else {
zag(p);
remove(tr[p].ls, key);
}
}
else p = 0;
}
else if(key < tr[p].key) remove(tr[p].ls, key);
else remove(tr[p].rs, key);
pushup(p); //每次操作完都要进行更新
}
int get_rank(int p, int key) {
if(!p) return 0;
if(key == tr[p].key) return tr[tr[p].ls].siz;
if(key < tr[p].key) return get_rank(tr[p].ls, key);
else return tr[tr[p].ls].siz + tr[p].cnt + get_rank(tr[p].rs, key);
}
int get_num(int p, int pos) {
if(!p) return inf;
if(tr[tr[p].ls].siz >= pos) return get_num(tr[p].ls, pos);
if(tr[tr[p].ls].siz + tr[p].cnt >= pos) return tr[p].key;
return get_num(tr[p].rs, pos - tr[tr[p].ls].siz - tr[p].cnt);
}
int get_pre(int p, int key) {
if(!p) return -inf;
if(key <= tr[p].key) return get_pre(tr[p].ls, key);
return max(tr[p].key, get_pre(tr[p].rs, key));
}
int get_next(int p, int key) {
if(!p) return inf;
if(key >= tr[p].key) return get_next(tr[p].rs, key);
return min(tr[p].key, get_next(tr[p].ls, key));
}
int main() {
build();
scanf("%d", &n);
int op, x;
while(n--) {
scanf("%d%d", &op, &x);
if(op == 1) insert(root, x);
else if(op == 2) remove(root, x);
else if(op == 3) printf("%d\n", get_rank(root, x));
else if(op == 4) printf("%d\n", get_num(root, x + 1)); //由于有负无穷的哨兵,所以要 +1 才是真正排名
else if(op == 5) printf("%d\n", get_pre(root, x));
else printf("%d\n", get_next(root, x));
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】