浅析 FHQ-Treap&Treap
前言
首先,带旋
左旋
如上图,现在我们把
来看看改变了什么,首先根(
:::success[Code]
inline int zag(rint u)
{
rint v=tr[u].r;
tr[u].r=tr[v].l;
tr[v].l=u;
pushup(u);
pushup(v);
return v;
}
:::
右旋
如上图,我们先不管具体的值,只看大小关系,我们需要将它从
如上图,现在右旋完了,我们对比一下看一下改变了啥,首先就是
:::success[Code]
inline int zig(rint u)
{
rint v=tr[u].l;
tr[u].l=tr[v].r;
tr[v].r=u;
pushup(u);
pushup(v);
return v;
}
:::
插入
首先,我们要保证 BST 性质不被破坏,那么我们就按照 BST 性质,找到一个空节点建立这个新插入的点,然后如果破坏了小根堆性质,那么就左旋或者右旋调整过来。
:::success[Code]
inline int insert(rint u,rint x)//当前的点为u,插入值为x的点
{
if(!u) return neo(x);
if(x==tr[u].x) tr[u].cnt++;//相等
else if(x<tr[u].x)
{
tr[u].l=insert(tr[u].l,x);
if(tr[tr[u].l].s>tr[u].s) u=zig(u);//左儿子优先级高,右旋
}
else
{
tr[u].r=insert(tr[u].r,x);
if(tr[u].s>tr[tr[u].r].s) u=zag(u);//右儿子优先级高,左旋
}
pushup(u);
return u;
}
:::
删除
这个很有意思,首先我们先根据 BST 的性质,找到这节点,我们分类讨论一下:
- 如果这个节点不止一个:
- 删掉就好。
- 如果只有一个:
- 如果是叶子节点:
- 删掉就好。
- 如果是有一个左节点:
- 将要删掉的节点左旋,然后就变成叶子节点,删掉就好。
- 如果是有一个右节点:
- 将要删掉的节点右旋,然后就变成叶子节点,删掉就好。
- 如果两个节点都有:
- 比较两个节点的优先级,优先级低的左旋或者右旋上去,然后要删掉的节点变成叶子节点,删掉就好。
:::success[Code]
inline int erase(rint u,rint x)
{
if(!u) return 0;//没这个节点
if(x<tr[u].x)//在左子树
{
tr[u].l=erase(tr[u].l,x);
}
else if(x>tr[u].x)//在右子树
{
tr[u].r=erase(tr[u].r,x);
}
else//就是这个节点
{
if(tr[u].cnt>1) tr[u].cnt--;//大于一个
else
{
if(!tr[u].l&&!tr[u].r) return 0;//删掉
else if(tr[u].l&&!tr[u].r)
{
u=zig(u);tr[u].r=erase(tr[u].r,x);//删除,注意到这里不是叶子节点还要继续右旋到叶子节点
}
else if(!tr[u].l&&tr[u].r)
{
u=zag(u);tr[u].l=erase(tr[u].l,x);//同理
}
else
{
if(tr[tr[u].l].s<tr[tr[u].r].s)//左边的小,右旋
{
u=zig(u);tr[u].r=erase(tr[u].r,x);
}
else
{
u=zag(u);tr[u].l=erase(tr[u].l,x);
}
}
}
}
pushup(u);
return u;
}
:::
查询排名
怎么查询
- 找到了:
- 返回之前和加上左子树的大小加上
1 。
- 返回之前和加上左子树的大小加上
- 如果在左子树:
- 继续找。
- 如果在右子树:
- 加上左子树和这个节点本身的大小继续找。
:::success[Code]
inline int find1(rint u,rint x)
{
if(!u) return 1;//没有x这个节点,排名1
if(x==tr[u].x)
{
return tr[tr[u].l].siz+1;//左子树大小加1
}
else if(x<tr[u].x)
{
return find1(tr[u].l,x);
}
else
{
return tr[tr[u].l].siz+tr[u].cnt+find1(tr[u].r,x);
}
}
:::
查询第 k 小
怎么查询第
:::success[Code]
inline int find2(rint u,rint k)
{
if(!u) return -114514;//没有这个排名
if(k<=tr[tr[u].l].siz)//在左子树中
{
return find2(tr[u].l,k);
}
else if(k<=tr[tr[u].l].siz+tr[u].cnt)//就是这个节点
{
return tr[u].x;
}
else//在右子树
{
return find2(tr[u].r,k-tr[tr[u].l].siz-tr[u].cnt);
}
}
:::
查询前驱
怎么查询
- 如果当前节点的值大于等于
x :- 说明
x 的前驱在左子树。
- 说明
- 否则,查询先查询右子树有没有比
x 小点的,最后和当前节点取\max 。
:::success[Code]
inline int pre(rint u,rint x)//查询 x 前驱
{
if(!u) return -1145141919810;//这个子树中没有 x 前驱
if(x<=tr[u].x) return pre(tr[u].l,x);//前驱不是右子树和当前节点
return max(tr[u].x,pre(tr[u].r,x));
}
:::
查询后继
怎么查询
- 如果当前节点小于等于
x :- 后继肯定不在左子树或者当前节点,去右子树看看。
- 如果当前节点大于
x :- 去左子树看看大于
x 的,和当前节点取\min 。
- 去左子树看看大于
:::success[Code]
inline int nxt(rint u,rint x)//查询x后继
{
if(!u) return 1145141919810;//这个子树没有 x 后继
if(tr[u].x<=x) return nxt(tr[u].r,x);
return min(tr[u].x,nxt(tr[u].l,x));
}
:::
完整代码
:::success[Ac Code]
#include <bits/stdc++.h>
using namespace std;
#ifdef __linux__
#define gc getchar_unlocked
#define pc putchar_unlocked
#else
#define gc _getchar_nolock
#define pc _putchar_nolock
#endif
#define R register
#define int long long
#define rint register int
#define _ read<int>()
inline bool blank(R const char &x){return !(x^32)||!(x^10)||!(x^13)||!(x^9);}
template<class T>inline T read()
{
R T r=0,f=1;R char c=gc();
while(!isdigit(c))
{
if(c=='-') f=-1;
c=gc();
}
while(isdigit(c)) r=(r<<1)+(r<<3)+(c^48),c=gc();
return f*r;
}
inline void out(rint x)
{
if(x<0) pc('-'),x=-x;
if(x<10) pc(x+'0');
else out(x/10),pc(x%10+'0');
}
inline void read(R char &x)
{
for(x=gc();blank(x)&&(x^-1);x=gc());
}
const int N=1e5+10;
mt19937 rnd(time(0)*114514/1919810);
int root=0;
struct Treap
{
struct Tree{int l,r,siz,cnt,x,s;}tr[N];
int cnt;
inline int neo(rint x)
{
tr[++cnt]={0,0,1,1,x,(int)rnd()};
return cnt;
}
inline void pushup(rint u){tr[u].siz=tr[tr[u].l].siz+tr[tr[u].r].siz+tr[u].cnt;}
inline int zag(rint u)//左旋
{
rint v=tr[u].r;
tr[u].r=tr[v].l;
tr[v].l=u;
pushup(u);pushup(v);
return v;
}
inline int zig(rint u)//右旋
{
rint v=tr[u].l;
tr[u].l=tr[v].r;
tr[v].r=u;
pushup(u);pushup(v);
return v;
}
inline int insert(rint u,rint x)//当前的点为u,插入值为x的点
{
if(!u) return neo(x);
if(x==tr[u].x) tr[u].cnt++;//相等
else if(x<tr[u].x)
{
tr[u].l=insert(tr[u].l,x);
if(tr[tr[u].l].s>tr[u].s) u=zig(u);//左儿子优先级高
}
else
{
tr[u].r=insert(tr[u].r,x);
if(tr[u].s>tr[tr[u].r].s) u=zag(u);//右儿子优先级第
}
pushup(u);
return u;
}
inline int erase(rint u,rint x)
{
if(!u) return 0;//没这个节点
if(x<tr[u].x)//在左子树
{
tr[u].l=erase(tr[u].l,x);
}
else if(x>tr[u].x)//在右子树
{
tr[u].r=erase(tr[u].r,x);
}
else//就是这个节点
{
if(tr[u].cnt>1) tr[u].cnt--;//大于一个
else
{
if(!tr[u].l&&!tr[u].r) return 0;//删掉
else if(tr[u].l&&!tr[u].r)
{
u=zig(u);tr[u].r=erase(tr[u].r,x);//删除,注意到这里不是叶子节点还要继续右旋旋到叶子节点
}
else if(!tr[u].l&&tr[u].r)
{
u=zag(u);tr[u].l=erase(tr[u].l,x);//同理
}
else
{
if(tr[tr[u].l].s<tr[tr[u].r].s)//左边的小,右旋
{
u=zig(u);tr[u].r=erase(tr[u].r,x);
}
else
{
u=zag(u);tr[u].l=erase(tr[u].l,x);
}
}
}
}
pushup(u);
return u;
}
inline int find1(rint u,rint x)
{
if(!u) return 1;//没有x这个节点,排名1
if(x==tr[u].x)
{
return tr[tr[u].l].siz+1;//左子树大小加1
}
else if(x<tr[u].x)
{
return find1(tr[u].l,x);
}
else
{
return tr[tr[u].l].siz+tr[u].cnt+find1(tr[u].r,x);
}
}
inline int find2(rint u,rint k)
{
if(!u) return -114514;//没有这个排名
if(k<=tr[tr[u].l].siz)//在左子树中
{
return find2(tr[u].l,k);
}
else if(k<=tr[tr[u].l].siz+tr[u].cnt)//就是这个节点
{
return tr[u].x;
}
else//在右子树
{
return find2(tr[u].r,k-tr[tr[u].l].siz-tr[u].cnt);
}
}
inline int pre(rint u,rint x)//查询 x 前驱
{
if(!u) return -1145141919810;//这个子树中没有 x 前驱
if(tr[u].x>=x) return pre(tr[u].l,x);//前驱不是右子树和当前节点
return max(tr[u].x,pre(tr[u].r,x));
}
inline int nxt(rint u,rint x)//查询x后继
{
if(!u) return 1145141919810;//这个子树没有 x 后继
if(tr[u].x<=x) return nxt(tr[u].r,x);
return min(tr[u].x,nxt(tr[u].l,x));
}
}tr;
signed main()
{
rint q=_;
while(q--)
{
rint op=_,x=_;
if(op==1)
{
root=tr.insert(root,x);
}
else if(op==2)
{
root=tr.erase(root,x);
}
else if(op==3)
{
out(tr.find1(root,x));pc('\n');
}
else if(op==4)
{
out(tr.find2(root,x));pc('\n');
}
else if(op==5)
{
out(tr.pre(root,x));pc('\n');
}
else
{
out(tr.nxt(root,x));pc('\n');
}
}
return 0;
}
:::
总结
个人认为这两种