【数据结构】fhq_treap
使用 fhq_treap 竟然让我一发就过了这题,nice,比之前编写 Splay 的时候对着 y总 代码 debug 的体验好多了。经此一役,不得不承认 fhq_treap 真的非常容易编写而且错误率低,不仅如此,它所能支持的操作也可以覆盖 Splay 所支持的,而且能避免很多复杂的边界问题,绝赞。
关于 fhq_treap,它和 Splay 同样具有两个核心操作,不过 Splay 的核心操作是 和 ,而 fhq_treap 的核心操作是 和 。
可以说是理解 和 之后 fhq_treap 就基本解决了。
下面简单介绍 和 。
分裂
通过传入一个值 ,利用这个 将传入的根节点 所在子树分割成两棵树,并将这两树的根节点编号记下来。
图解:
一开始树的结构:
当传入的值 时,分裂出的两棵树:
结合代码实现理解:
void split(int u, int val, int &x, int &y){
if(!u) return x=y=0, void(); // 当走到空节点的时候返回。
if(tr[u].v<=val)
x=u, split(rs, val, rs, y); // 向右子树分裂
else
y=u, split(ls, val, x, ls); // 向左子树分裂
pushup(u);
}
合并
合并根节点编号为 和 的两个子树并返回新树的根节点编号。
int merge(int x, int y){
if(!x || !y) return x+y; // 如果有一个是空节点,返回另一个节点编号。
if(tr[x].key>tr[y].key){ // 按照优先级来决定哪个作为根节点。
tr[x].r=merge(tr[x].r, y);
pushup(x);
return x;
}
else{
tr[y].l=merge(x, tr[y].l);
pushup(y);
return y;
}
}
全部代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
struct Node{
int l, r;
int key;
int sz, v;
#define ls tr[u].l
#define rs tr[u].r
}tr[N];
int root, idx;
void pushup(int u){
tr[u].sz=tr[ls].sz+tr[rs].sz+1;
}
int add(int v){
++idx;
tr[idx].key=rand(), tr[idx].sz=1, tr[idx].v=v;
return idx;
}
int merge(int x, int y){
if(!x || !y) return x+y;
if(tr[x].key>tr[y].key){
tr[x].r=merge(tr[x].r, y);
pushup(x);
return x;
}
else{
tr[y].l=merge(x, tr[y].l);
pushup(y);
return y;
}
}
void split(int u, int val, int &x, int &y){
if(!u) return x=y=0, void();
if(tr[u].v<=val)
x=u, split(rs, val, rs, y);
else
y=u, split(ls, val, x, ls);
pushup(u);
}
void insert(int v){
int x, y;
split(root, v, x, y);
root=merge(x, merge(add(v), y));
}
void remove(int v){
int x, y, z;
split(root, v, x, z);
split(x, v-1, x, y);
y=merge(tr[y].l, tr[y].r);
root=merge(merge(x, y), z);
}
int val4rk(int v){
int x, y;
split(root, v-1, x, y);
int res=tr[x].sz+1;
root=merge(x, y);
return res;
}
int rk4val(int k){
int u=root;
while(u){
if(tr[ls].sz+1==k) return tr[u].v;
else if(tr[ls].sz>=k) u=ls;
else k-=tr[ls].sz+1, u=rs;
}
return -1;
}
int get_prev(int v){
int x, y;
split(root, v-1, x, y);
int u=x;
while(rs) u=rs;
int res=tr[u].v;
root=merge(x, y);
return res;
}
int get_next(int v){
int x, y;
split(root, v, x, y);
int u=y;
while(ls) u=ls;
int res=tr[u].v;
root=merge(x, y);
return res;
}
int main(){
ios::sync_with_stdio(false);
srand(131);
int q; cin>>q;
while(q--){
int op, x; cin>>op>>x;
if(op==1) insert(x);
else if(op==2) remove(x);
else if(op==3) cout<<val4rk(x)<<endl;
else if(op==4) cout<<rk4val(x)<<endl;
else if(op==5) cout<<get_prev(x)<<endl;
else if(op==6) cout<<get_next(x)<<endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】