zoukankan      html  css  js  c++  java
  • 简单数据结构模板汇总

    不包括字符串和图论内容。
    某种意义上算是“简单”数据结构……
    代码压行警告qwq

    如果存在与数据结构有关的经典算法,也会予以列出。

    1. 单调队列 (O(n))

    luoguP1886

    单调队列和单调栈的讲解在这里

    const int maxn=1000010;
    int n,k,cnt,a[maxn],minans[maxn],maxans[maxn];
    deque<int> minint,maxint;
    void push(int pos)
    {
        while(!minint.empty()&&minint.front()<=pos-k)minint.pop_front();
        while(!maxint.empty()&&maxint.front()<=pos-k)maxint.pop_front();
        while(!minint.empty()&&a[minint.back()]>=a[pos])minint.pop_back();
        while(!maxint.empty()&&a[maxint.back()]<=a[pos])maxint.pop_back();
        minint.push_back(pos);maxint.push_back(pos);
    }
    //......
    for(int i=1;i<k;i++)push(i);
    for(int i=k;i<=n;i++)
    {
        push(i);cnt++;
        minans[cnt]=a[minint.front()];
        maxans[cnt]=a[maxint.front()];
    }
    

    2. 单调栈 (O(n))

    luoguP5788

    const int maxn=3000010;
    int n,a[maxn],ans[maxn];
    stack<int> st;
    
for(int i=n;i>=1;i--)
    {
        while(!st.empty()&&a[st.top()]<=a[i])st.pop();
        if(!st.empty())ans[i]=st.top();
        st.push(i);
    }
    

    3. 并查集 (O(nalpha(n)))

    luoguP3367

    路径压缩+启发式合并
    玄学的时间复杂度分析

    const int maxn=100010;
    int n,fa[maxn],siz[maxn];
    void init(int xx){for(int i=1;i<=xx;i++){fa[i]=i;siz[i]=1;}}
    int find(int xx){return fa[xx]==xx?xx:fa[xx]=find(fa[xx]);}
    int query(int xx,int yy){return find(xx)==find(yy);}
    void merge(int xx,int yy)
    {
        int fx=find(xx),fy=find(yy);
        if(fx==fy)return;
        if(siz[fx]<siz[fy]){fa[fx]=fy;siz[fy]+=siz[fx];fa[xx]=fy;}
        else{fa[fy]=fx;siz[fx]+=siz[fy];fa[yy]=fx;}
    }
    

    4. ST表 (O(nlog n)-O(1))

    luoguP3865

    对ST表和树状数组的简单讲解

    int n,m,ans,l,r,k,lg2[100010],st[100010][20];
    for(int i=2;i<100010;i++)lg2[i]=lg2[i/2]+1;
    for(int i=1;i<=n;i++)scanf("%d",&st[i][0]);
    for(int j=1;j<=lg2[n];j++)
    	for(int i=1;i+(1<<(j-1))<=n;i++)
    		st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    for(int i=1;i<=m;i++)
    {
    	scanf("%d%d",&l,&r);k=lg2[r-l+1];
    	ans=max(st[l][k],st[r-(1<<k)+1][k]);
    	printf("%d
    ",ans);
    }
    

    5. 树状数组

    5.1 树状数组模板 (O(nlog n)-O(log n))

    luoguP3374

    预处理可以(O(n)),但没必要。

    int n,m,a[500010];
    inline int lowbit(int x){return x&(-x);}
    inline void add(int x,int k){while(x<=n)a[x]+=k,x+=lowbit(x);}
    inline int query(int pos)
    {
        int ans=0;
        while(pos)ans+=a[pos],pos-=lowbit(pos);
        return ans;
    }
    

    5.2 树状数组求逆序对 (O(nlog n))

    luoguP1908

    建议遇到这种题直接归并排序(

    struct node{int pos,x;}a[500010];
    int rk[500010];
    bool cmp(node xx,node yy)
    {
        if(xx.x!=yy.x)return xx.x<yy.x;
        return xx.pos<yy.pos;
    }
    class Bittree
    {
    public:
        int num=500010,datas[500010];
        int lowbit(int x){return x&(-x);}
        void add(int x,int k){while(x<=num)datas[x]+=k,x+=lowbit(x);}
        int query(int pos)
        {
            int ans=0;
            while(pos)ans+=datas[pos],pos-=lowbit(pos);
            return ans;
        }
    }tree;
    int n,ans;
    //......
    for(int i=1;i<=n;i++){scanf("%lld",&a[i].x);a[i].pos=i;}
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)rk[a[i].pos]=i;
    for(int i=1;i<=n;i++){tree.add(rk[i],1);ans+=i-tree.query(rk[i]);}
    

    6. 线段树

    6.1 复杂标记线段树 (O(n)-O(log n))

    luoguP3373为例。
    高度模式化的代码。

    const int maxn=100010;
    int n,m,p,a[maxn];
    struct node{int l,r,val,add,mul;}tree[maxn<<2];
    void pushup(int x)
    {
        int lson=x<<1,rson=lson|1;
        tree[x].val=(tree[lson].val+tree[rson].val)%p;
    }
    void pushmul(int x,int k)
    {
        tree[x].val*=k;tree[x].val%=p;
        tree[x].mul*=k;tree[x].mul%=p;
        tree[x].add*=k;tree[x].add%=p;
    }
    void pushadd(int x,int k)
    {
        tree[x].val+=k*(tree[x].r-tree[x].l+1);tree[x].val%=p;
        tree[x].add+=k;tree[x].add%=p;
    }
    void pushdown(int x)
    {
        int lson=x<<1,rson=lson|1;
        if(tree[x].l==tree[x].r)return;
        if(tree[x].mul!=1)//if的先后是按标记的优先级排的
        {
            pushmul(lson,tree[x].mul);
            pushmul(rson,tree[x].mul);
            tree[x].mul=1;
        }
        if(tree[x].add!=0)
        {
            pushadd(lson,tree[x].add);
            pushadd(rson,tree[x].add);
            tree[x].add=0;
        }
    }
    void build(int x,int l,int r)
    {
        tree[x]=(node){l,r,0,0,1};
        if(l==r){tree[x].val=a[l];return;}
        int mid=(tree[x].l+tree[x].r)>>1,lson=x<<1,rson=lson|1;
        build(lson,l,mid);build(rson,mid+1,r);
        pushup(x);
    }
    void modify_mul(int x,int l,int r,int k)
    {
        pushdown(x);
        if(l<=tree[x].l&&r>=tree[x].r){pushmul(x,k);return;}
        int mid=(tree[x].l+tree[x].r)>>1,lson=x<<1,rson=lson|1;
        if(l<=mid)modify_mul(lson,l,r,k);
        if(r>mid)modify_mul(rson,l,r,k);
        pushup(x);
    }
    void modify_add(int x,int l,int r,int k)
    {
        pushdown(x);
        if(l<=tree[x].l&&r>=tree[x].r){pushadd(x,k);return;}
        int mid=(tree[x].l+tree[x].r)>>1,lson=x<<1,rson=lson|1;
        if(l<=mid)modify_add(lson,l,r,k);
        if(r>mid)modify_add(rson,l,r,k);
        pushup(x);
    }
    int query_sum(int x,int l,int r)
    {
        pushdown(x);
        if(l<=tree[x].l&&r>=tree[x].r)return tree[x].val;
        int mid=(tree[x].l+tree[x].r)>>1,lson=x<<1,rson=lson|1,ans=0;
        if(l<=mid){ans+=query_sum(lson,l,r);ans%=p;}
        if(r>mid){ans+=query_sum(rson,l,r);ans%=p;}
        return ans;
    }
    

    6.2 动态开点权值线段树 (O(nlog k))

    luoguP3369
    有些询问可以用map模拟,但这样写确实是原汁原味的权值线段树。
    加强版卡空间过不去qaq

    const int top=1e7+1;//根据值域进行相应改动
    int n,cnt,rt;
    struct node{int l,r,val;}tree[5000010];//看值域开,别MLE就行
    void modify(int &k,int x,int l,int r,int add)
    {
        if(!k)k=++cnt;
        tree[k].val+=add;
        if(l==r)return;
        int mid=(l+r)>>1;
        if(x<=mid)modify(tree[k].l,x,l,mid,add);
        else modify(tree[k].r,x,mid+1,r,add);
    }
    int query(int k,int treel,int treer,int l,int r)
    {
        if(!k)return 0;
        if(l<=treel&&r>=treer)return tree[k].val;
        int mid=(treel+treer)>>1,ans=0;
        if(l<=mid)ans+=query(tree[k].l,treel,mid,l,r);
        if(r>mid)ans+=query(tree[k].r,mid+1,treer,l,r);
        return ans;
    }
    int getnum(int k,int x,int l,int r)
    {
        if(!k)return 0;
        if(l==r)return l;
        int mid=(l+r)>>1;
        if(tree[tree[k].l].val>=x)return getnum(tree[k].l,x,l,mid);
        else return getnum(tree[k].r,x-tree[tree[k].l].val,mid+1,r);
    }
    int getrank(int x){return query(rt,1,2e7+1,1,x-1)+1;}//这里的询问也是根据值域进行改动
    int getpre(int x){return getnum(rt,getrank(x)-1,1,2e7+1);}
    int getsuc(int x){return getnum(rt,getrank(x+1),1,2e7+1);}
    

    6.3 扫描线

    7. 树链剖分

    7.1 轻重链剖分 (O(nlog^2 n))

    luoguP3384

    const int maxn=100010;
    int n,m,rt,p,cnt,dfncnt,a[maxn],w[maxn],h[maxn],dep[maxn],siz[maxn],son[maxn],fa[maxn],top[maxn],dfn[maxn];
    struct node{int l,r,val,add;}tree[maxn<<2];
    void pushup(int x)
    {
        int lson=x<<1,rson=lson|1;
        tree[x].val=(tree[lson].val+tree[rson].val)%p;
    }
    void pushadd(int x,int k)
    {
        tree[x].val+=k%p*(tree[x].r-tree[x].l+1)%p;tree[x].val%=p;
        tree[x].add+=k;tree[x].add%=p;
    }
    void pushdown(int x)
    {
        int lson=x<<1,rson=lson|1;
        if(tree[x].l==tree[x].r)return;
        if(tree[x].add!=0)
        {
            pushadd(lson,tree[x].add);
            pushadd(rson,tree[x].add);
            tree[x].add=0;
        }
    }
    void build(int x,int l,int r)
    {
        tree[x]=(node){l,r,0,0};
        if(l==r){tree[x].val=a[l]%p;return;}
        int mid=(l+r)>>1,lson=x<<1,rson=lson|1;
        build(lson,l,mid);build(rson,mid+1,r);
        pushup(x);
    }
    void modify(int x,int l,int r,int k)
    {
        pushdown(x);
        if(l<=tree[x].l&&r>=tree[x].r){pushadd(x,k);return;}
        int mid=(tree[x].l+tree[x].r)>>1,lson=x<<1,rson=lson|1;
        if(l<=mid)modify(lson,l,r,k);
        if(r>mid)modify(rson,l,r,k);
        pushup(x);
    }
    int query(int x,int l,int r)
    {
        pushdown(x);
        if(l<=tree[x].l&&r>=tree[x].r)return tree[x].val%p;
        int mid=(tree[x].l+tree[x].r)>>1,lson=x<<1,rson=lson|1,ans=0;
        if(l<=mid){ans+=query(lson,l,r);ans%=p;}
        if(r>mid){ans+=query(rson,l,r);ans%=p;}
        return ans;
    }
    struct edge{int to,nxt;}e[maxn<<1];
    void addedge(int u,int v){e[++cnt]=(edge){v,h[u]};h[u]=cnt;}
    void add(int u,int v){addedge(u,v);addedge(v,u);}
    void dfs1(int u,int f)
    {
        fa[u]=f;siz[u]++;dep[u]=dep[f]+1;
        int maxsiz=0;
        for(int i=h[u];i;i=e[i].nxt)
        {
            int p=e[i].to;
            if(p!=f)
            {
                dfs1(p,u);siz[u]+=siz[p];
                if(maxsiz<siz[p]){maxsiz=siz[p];son[u]=p;}
            }
        }
    }
    void dfs2(int u,int f)
    {
        top[u]=f;dfn[u]=++dfncnt;
        if(w[u])modify(1,dfn[u],dfn[u],w[u]);
        if(!son[u])return;
        dfs2(son[u],f);
        for(int i=h[u];i;i=e[i].nxt)
        {
            int p=e[i].to;
            if(p!=son[u]&&p!=fa[u])dfs2(p,p);
        }
    }
    int lca(int u,int v)//这个操作是模板题所没有的,但是轻重链剖分也资瓷,是O(nlogn)的
    {
        while(top[u]!=top[v])
        {
            if(dep[top[u]]<dep[top[v]])swap(u,v);
            u=fa[top[u]];
        }
        if(dep[u]>dep[v])swap(u,v);
        return u;
    }
    void addpath(int u,int v,int k)
    {
        while(top[u]!=top[v])
        {
            if(dep[top[u]]<dep[top[v]])swap(u,v);
            modify(1,dfn[top[u]],dfn[u],k%p);
            u=fa[top[u]];
        }
        if(dep[u]>dep[v])swap(u,v);
        modify(1,dfn[u],dfn[v],k);
    }
    int querypath(int u,int v)
    {
        int ans=0;
        while(top[u]!=top[v])
        {
            if(dep[top[u]]<dep[top[v]])swap(u,v);
            ans+=query(1,dfn[top[u]],dfn[u]);ans%=p;
            u=fa[top[u]];
        }
        if(dep[u]>dep[v])swap(u,v);
        ans+=query(1,dfn[u],dfn[v]);ans%=p;
        return ans;
    }
    void addtree(int u,int k){modify(1,dfn[u],dfn[u]+siz[u]-1,k%p);}
    int querytree(int u){return query(1,dfn[u],dfn[u]+siz[u]-1);}
    

    7.2 dsu on tree/静态链分治

    CF600E

    7.3 长链剖分

    8. 分块相关

    见这里

    9. 左偏树

    luoguP3377
    对于这道题,左偏树需要与并查集一起使用。

    10. 平衡树

    10.1 普通平衡树 (O(nlog n))

    luoguP6136
    快进到数据加强版QwQ
    讲道理这个隐藏的数据范围是真的坑人……

    10.1.1 fhq_treap版

    const int maxn=2000010;
    int ncnt,rt;
    struct fhq_treap{int l,r,val,key,siz;}tree[maxn];
    inline void update(int x){tree[x].siz=tree[tree[x].l].siz+tree[tree[x].r].siz+1;}
    inline void build(int &x,int val)
    {
        x=++ncnt;
        tree[x].val=val;
        tree[x].key=rand()%10000000;
        tree[x].siz=1;
    }
    void split(int x,int val,int &rx,int &ry)
    {
        if(!x){rx=ry=0;return;}
        if(tree[x].val<=val){rx=x;split(tree[x].r,val,tree[x].r,ry);}
        else{ry=x;split(tree[x].l,val,rx,tree[x].l);}
        update(x);
    }
    int merge(int x,int y)
    {
        if(!x||!y)return x+y;
        if(tree[x].key<tree[y].key)
        {
            tree[x].r=merge(tree[x].r,y);
            update(x);return x;
        }
        else
        {
            tree[y].l=merge(x,tree[y].l);
            update(y);return y;
        }
    }
    void ins(int val)
    {
        int now,x,y;
        split(rt,val,x,y);build(now,val);
        rt=merge(merge(x,now),y);
    }
    void del(int val)
    {
        int x,y,z;
        split(rt,val,x,z);split(x,val-1,x,y);
        y=merge(tree[y].l,tree[y].r);
        rt=merge(merge(x,y),z);
    }
    int getrank(int val)
    {
        int rk,x,y;split(rt,val-1,x,y);
        rk=tree[x].siz+1;rt=merge(x,y);
        return rk;
    }
    int getnum(int rk)
    {
        int now=rt;
        while(now)
        {
            int lsiz=tree[tree[now].l].siz;
            if(lsiz+1==rk)break;
            else if(rk<=lsiz)now=tree[now].l;
            else{rk-=lsiz+1;now=tree[now].r;}
        }
        return tree[now].val;
    }
    int getpre(int val){return getnum(getrank(val)-1);}
    int getsuc(int val){return getnum(getrank(val+1));}
    //...
    srand(19260817);//玄学数字
    

    10.1.2 splay版

    const int maxn=2000010;
    int ncnt,rt;
    struct splay{int fa,ch[2],val,cnt,siz;}tree[maxn];
    inline bool judge(int x,int f){return tree[f].ch[1]==x;}
    inline void con(int x,int f,int s){tree[f].ch[s]=x;tree[x].fa=f;}
    inline void update(int x){tree[x].siz=tree[tree[x].ch[0]].siz+tree[tree[x].ch[1]].siz+tree[x].cnt;}
    inline void build(int &x,int f,int val)
    {
        x=++ncnt;
        tree[x].fa=f;tree[x].val=val;
        tree[x].siz=tree[x].cnt=1;
    }
    void rotate(int x)
    {
        int f=tree[x].fa,g=tree[f].fa,k=judge(x,f);
        con(tree[x].ch[k^1],f,k);con(x,g,judge(f,g));con(f,x,k^1);
        update(f);update(x);
    }
    void splaying(int x,int top)
    {
        if(!top)rt=x;
        while(tree[x].fa!=top)
        {
            int f=tree[x].fa,g=tree[f].fa;
            if(g!=top)judge(x,f)^judge(f,g)?rotate(x):rotate(f);
            rotate(x);
        }
    }
    void ins(int val,int &x,int fa)
    {
        if(!x){build(x,fa,val);splaying(x,0);}
        else if(val<tree[x].val)ins(val,tree[x].ch[0],x);
        else if(val>tree[x].val)ins(val,tree[x].ch[1],x);
        else{tree[x].cnt++;splaying(x,0);}
    }
    void delnode(int x)
    {
        splaying(x,0);
        if(tree[x].cnt>1)tree[x].cnt--;
        else if(tree[x].ch[1])
        {
            int now=tree[x].ch[1];
            while(tree[now].ch[0])now=tree[now].ch[0];
            splaying(now,x);con(tree[x].ch[0],now,0);
            rt=now;tree[now].fa=0;update(rt);
        }
        else{rt=tree[x].ch[0];tree[rt].fa=0;}
    }
    void del(int val,int x)
    {
        if(val==tree[x].val)delnode(x);
        else if(val<tree[x].val)del(val,tree[x].ch[0]);
        else del(val,tree[x].ch[1]);
    }
    int getrank(int val)
    {
        int now=rt,rk=1;
        while(now)
        {
            int lsiz=tree[tree[now].ch[0]].siz;
            if(val==tree[now].val){rk+=lsiz;splaying(now,0);break;}
            else if(val<tree[now].val)now=tree[now].ch[0];
            else
            {
                rk+=lsiz+tree[now].cnt;
                now=tree[now].ch[1];
            }
        }
        return rk;
    }
    int getnum(int rk)
    {
        int now=rt;
        while(now)
        {
            int lsiz=tree[tree[now].ch[0]].siz;
            if(lsiz+1<=rk&&rk<=lsiz+tree[now].cnt){splaying(now,0);break;}
            else if(rk<=lsiz)now=tree[now].ch[0];
            else
            {
                rk-=lsiz+tree[now].cnt;
                now=tree[now].ch[1];
            }
        }
        return tree[now].val;
    }
    int getpre(int val){return getnum(getrank(val)-1);}
    int getsuc(int val){return getnum(getrank(val+1));}
    

    10.2 文艺平衡树

    10.2.1 fhq_treap版

    10.2.2 splay版

    11. 可持久化数据结构

    11.1 可持久化数组 (O(nlog n))

    luoguP3919

    const int maxn=1000010;
    int n,cnt,rt[maxn],a[maxn];
    struct node{int l,r,val;}tree[maxn*20];
    void build(int &x,int l,int r)
    {
        x=++cnt;
        if(l==r){tree[x].val=a[l];return;}
        int mid=(l+r)>>1;
        build(tree[x].l,l,mid);
        build(tree[x].r,mid+1,r);
    }
    void modify(int &k,int ver,int x,int l,int r,int val)
    {
        k=++cnt;tree[k]=tree[ver];
        if(l==r){tree[k].val=val;return;}
        int mid=(l+r)>>1;
        if(x<=mid)modify(tree[k].l,tree[ver].l,x,l,mid,val);
        else modify(tree[k].r,tree[ver].r,x,mid+1,r,val);
    }
    int query(int k,int x,int l,int r)
    {
        if(l==r)return tree[k].val;
        int mid=(l+r)>>1;
        if(x<=mid)return query(tree[k].l,x,l,mid);
        else return query(tree[k].r,x,mid+1,r);
    }
    

    11.2 主席树

    11.3 可持久化并查集 (O(nlog^2 n))

    luoguP3402

    不路径压缩+按秩合并的并查集。
    就是个可持久化数组的简单应用。
    时间复杂度口胡:
    考虑将节点按照完全二叉树的方式进行合并。
    则此时因为只有按秩合并,树高显然为(log n)级别的。
    然后不停地query高度最小的节点,于是find函数也需要(log n)次递归,而可持久化数组的单次查询操作是(O(log n))的。
    则find一次的时间复杂度为(O(log^2 n)),总的时间复杂度为(O(nlog^2 n))

    const int maxn=100010;
    int n;
    class ver_vector
    {
    public:
        int cnt,rt[maxn],a[maxn];
        struct node{int l,r,val;}tree[maxn*20];
        void build(int &x,int l,int r)
        {
            x=++cnt;
            if(l==r){tree[x].val=a[l];return;}
            int mid=(l+r)>>1;
            build(tree[x].l,l,mid);
            build(tree[x].r,mid+1,r);
        }
        void modify(int &k,int ver,int x,int l,int r,int val)
        {
            k=++cnt;tree[k]=tree[ver];
            if(l==r){tree[k].val=val;return;}
            int mid=(l+r)>>1;
            if(x<=mid)modify(tree[k].l,tree[ver].l,x,l,mid,val);
            else modify(tree[k].r,tree[ver].r,x,mid+1,r,val);
        }
        int query(int k,int x,int l,int r)
        {
            if(l==r)return tree[k].val;
            int mid=(l+r)>>1;
            if(x<=mid)return query(tree[k].l,x,l,mid);
            else return query(tree[k].r,x,mid+1,r);
        }
    }fa,height;
    int find(int x,int ver)
    {
        int f=fa.query(fa.rt[ver],x,1,n);
        if(f==x)return f;
        else return find(f,ver);
    }
    void merge(int ver,int x,int y)
    {
        int fx=find(x,ver-1),fy=find(y,ver-1);
        if(fx==fy)
        {
            fa.rt[ver]=fa.rt[ver-1];
            height.rt[ver]=height.rt[ver-1];
            return;
        }
        int hx=height.query(height.rt[ver-1],fx,1,n),hy=height.query(height.rt[ver-1],fy,1,n);
        if(hx<hy)
        {
            fa.modify(fa.rt[ver],fa.rt[ver-1],fx,1,n,fy);
            height.rt[ver]=height.rt[ver-1];
        }
        else if(hx>hy)
        {
            fa.modify(fa.rt[ver],fa.rt[ver-1],fy,1,n,fx);
            height.rt[ver]=height.rt[ver-1];
        }
        else
        {
            fa.modify(fa.rt[ver],fa.rt[ver-1],fx,1,n,fy);
            height.modify(height.rt[ver],height.rt[ver-1],fy,1,n,hy+1);
        }
    }
    

    11.4 可持久化平衡树

    11.5 可持久化文艺平衡树

  • 相关阅读:
    进程 线程
    random模块 时间模块 sys模块 os模块 json模块 pickle模块
    异常处理
    面向对象进阶篇
    面向对象初级篇
    正则表达式
    re模块的相关知识
    CNN归纳偏好
    window和Linux下安装nvidia的apex
    使用GrabCut做分割
  • 原文地址:https://www.cnblogs.com/pjykk/p/14988606.html
Copyright © 2011-2022 走看看