可持久化数据结构
Posted KaaaterinaX
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了可持久化数据结构相关的知识,希望对你有一定的参考价值。
可持久化数据结构指的是支持查询所有历史版本的一种数据结构,一般情况下只有更新信息但整体拓扑结构不变的数据结构能实现可持久化。
可持久化线段树(主席树)
题意简而言之就是给定序列中的一个区间,输出这个区间第k大的数。
这是一个非常经典的问题,运用了可持久化线段树结构以及树上二分的思想。
可以把思路拆开来看。
树上二分:
查询某个区间内第k大的数,可以把有序区间分成两半来看,如果在mid之前的数比k个多,那么要求的数一定在mid之前,继续进行同样的操作直到区间只包含一个数。
线段树:
由上述思路可以发现,这里的线段树需要区间内数字的个数,并且限制了可查区间,那么就要用到可持久化线段树啦。
代码如下:
//可持久化线段树
//树上二分
const int maxn=2e5+7;
int a[maxn];//原序列
//离散化
vector<int> num;
int find(int x){
return (int)(lower_bound(num.begin(), num.end(),x)-num.begin());
}
//线段树
struct node{
int l,r;//左儿子和右儿子
int cnt;//这个区间内有多少个数
}tr[maxn*4+maxn*17];
int root[maxn];//记录每个节点的根节点
int idx;//新建的节点编号
int build(int l0,int r0){
//[l0,r0]表示当前节点区间
int p=++idx;
if(l0==r0){
return p;
}
int mid=(l0+r0)>>1;
tr[p].l=build(l0,mid);
tr[p].r=build(mid+1,r0);
return p;
}
int insert(int p,int l0,int r0,int x){
//插入元素,实现可持久化数据结构
//p是这个当前节点上一个版本的编号
//x是需要修改的节点编号
//insert函数返回新创建的节点编号
int q=++idx;
tr[q]=tr[p];
if(l0==r0){
tr[q].cnt++;
return q;
}
int mid=(l0+r0)>>1;
if(x<=mid){
tr[q].l=insert(tr[p].l,l0,mid,x);
}
else{
tr[q].r=insert(tr[p].r,mid+1,r0,x);
}
tr[q].cnt=tr[tr[q].l].cnt+tr[tr[q].r].cnt;//因为有新的变化
return q;
}
int query(int l0,int r0,int l,int r,int k){
//树上二分
//[l0,r0]表示查询区间
//l,r为查询版本区间
if(l0==r0){
return l0;
}
int cntl=tr[tr[r].l].cnt-tr[tr[l].l].cnt;//在这个查询区间内,左儿子的cnt
int mid=(l0+r0)>>1;
if(cntl>=k){
return query(l0,mid,tr[l].l,tr[r].l,k);
}else{
k-=cntl;
return query(mid+1,r0,tr[l].r,tr[r].r,k);
}
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
num.pb(a[i]);
}
sort(num.begin(),num.end());
num.erase(unique(num.begin(),num.end()),num.end());
root[0]=build(0,(int)num.size()-1);//初版线段树
for(int i=1;i<=n;i++){
root[i]=insert(root[i-1],0,(int)num.size()-1,find(a[i]));
}
while(m--){
int l,r,k;
cin>>l>>r>>k;
cout<<num[query(0,(int)num.size()-1,root[l-1],root[r],k)]<<endl;
}
}
可持久化字典树
以上是关于可持久化数据结构的主要内容,如果未能解决你的问题,请参考以下文章
SpringCloud系列十一:SpringCloudStream(SpringCloudStream 简介创建消息生产者创建消息消费者自定义消息通道分组与持久化设置 RoutingKey)(代码片段