专栏文章
学习笔记-数据结构
算法·理论参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mimzrfas
- 此快照首次捕获于
- 2025/12/01 18:13 3 个月前
- 此快照最后确认于
- 2025/12/01 18:13 3 个月前
数据结构
真的非常非常重要。计算机科学等于算法(Algorithm)加数据结构(Data Structure)。
基础数据结构
1. 栈
蒟蒻学的第一个数据结构。
特点,先进先出,只有栈顶可以访问。
下面给出模板题 B3614 的代码。
STL 实现(优点:好写,维护成本低。缺点:常数大,容易报错):
CPP#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
int T;
void solve()
{
stack<ull> st;
int N; cin>>N;
while(N--){
string s; cin>>s;
if(s=="push"){
ull x; cin>>x;
st.push(x);
}
if(s=="pop"){
if(st.empty()) cout<<"Empty\n";
else st.pop();
}
if(s=="query"){
if(st.empty()) cout<<"Anguei!\n";
else cout<<st.top()<<'\n';
}
if(s=="size"){
cout<<st.size()<<'\n';
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>T;
while(T--) solve();
}
手写实现(优点:常数小,自定义程度高,缺点:难以维护):
CPP#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
class Stack{
private:
ull st[1000005], top;
public:
void init(){
top=0;
}
inline void push(ull x){
st[++top]=x;
}
inline void pop(){
if(top) top--;
else cout<<"Empty\n";
}
inline ull query(){
if(top) return st[top];
cout<<"Anguei!\n";
return -1;
}
inline ull size(){
return top;
}
inline bool empty(){
return top==0;
}
}S;
int T;
void solve()
{
S.init();
int N; cin>>N;
while(N--){
string s; cin>>s;
if(s=="push"){
ull x; cin>>x;
S.push(x);
}
if(s=="pop"){
S.pop();
}
if(s=="query"){
ull x=S.query();
if(~x) cout<<x<<'\n';
}
if(s=="size"){
cout<<S.size()<<'\n';
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>T;
while(T--) solve();
}
2. 队列
先进后出表,和栈一样运用广泛。
STL 实现:
CPP#include<bits/stdc++.h>
using namespace std;
int N;
queue<int> q;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>N;
while(N--){
int op; cin>>op;
if(op==1){
int x; cin>>x;
q.push(x);
}
if(op==2){
if(q.empty()) cout<<"ERR_CANNOT_POP\n";
else q.pop();
}
if(op==3){
if(q.empty()) cout<<"ERR_CANNOT_QUERY\n";
else cout<<q.front()<<'\n';
}
if(op==4){
cout<<q.size()<<'\n';
}
}
}
手写实现:
CPP#include<bits/stdc++.h>
using namespace std;
class Queue{
private:
int q[10005],l=0,r=-1;
public:
void init(){
l=0,r=-1;
}
inline void push(int x){
q[++r]=x;
}
inline void pop(){
if(l>r) cout<<"ERR_CANNOT_POP\n";
else l++;
}
inline int query(){
if(l>r){
cout<<"ERR_CANNOT_QUERY\n";
return -1;
}
return q[l];
}
inline int size(){
return r-l+1;
}
inline bool empty(){
return l>r;
}
}Q;
int N;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>N;
while(N--){
int op; cin>>op;
if(op==1){
int x; cin>>x;
Q.push(x);
}
if(op==2){
Q.pop();
}
if(op==3){
int x=Q.query();
if(~x) cout<<x<<'\n';
}
if(op==4){
cout<<Q.size()<<'\n';
}
}
}
3. 链表
通过记录每一个节点的前驱和后继实现 插入,遍历。
码略。
4. 二叉堆(优先队列)
通过对二叉树节点的上浮和下沉实现每次顶端都是最值。
插入,删除都是 ,查询堆顶是 的。
一般用优先队列(
CPPstd::priority_queue)实现,代码如下:#include<bits/stdc++.h>
using namespace std;
int N;
priority_queue<int,vector<int>,greater<int>> q;
int main()
{
cin>>N;
while(N--){
int op; cin>>op;
if(op==1){
int x; cin>>x;
q.push(x);
}
if(op==2) cout<<q.top()<<'\n';
if(op==3) q.pop();
}
}
高级数据结构
1. 并查集
1.1. 并查集
用于解决集合合并,查询两元素是否在一个集合。
模板代码:
CPP#include<bits/stdc++.h>
using namespace std;
int N,M;
namespace DSU
{
int fa[200005];
void init(){
for(int i=1;i<=N;i++) fa[i]=i;
}
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y){
int fx=find(x), fy=find(y);
if(fx==fy) return ;
fa[fx]=fy;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>N>>M;
DSU::init();
while(M--){
int z,x,y; cin>>z>>x>>y;
if(z==1) DSU::merge(x,y);
if(z==2){
int fx=DSU::find(x), fy=DSU::find(y);
if(fx==fy) cout<<"Y\n";
else cout<<"N\n";
}
}
}
1.2. 扩展域并查集
通过对元素拓展多个域进行合并,解决朋友,敌人,捕食等多种复杂关系。
P1892 代码:
CPP#include<bits/stdc++.h>
using namespace std;
int N,M,P;
int fa[2005];
int find(int x){fu
if(x==fa[x]) return x;
else return fa[x]=find(fa[x]);
}
int main()
{
cin>>N>>M;
for(int i=1;i<=2*N;i++) fa[i]=i;
for(int i=1;i<=M;i++){
char ch;
int x,y;
cin>>ch>>x>>y;
if(ch=='F') fa[find(y)]=find(x);
else{
fa[find(N+x)]=find(y);
fa[find(N+y)]=find(x);
}
}
for(int i=1;i<=N;i++){
if(fa[i]==i) P++;
}
cout<<P;
return 0;
}
1.3. 带权并查集
记录自己与父亲节点的边权,以处理更复杂的关系。
扩展域并查集与带权并查集的适用范围差不多。
P2024 代码:
CPP#include<bits/stdc++.h>
using namespace std;
int N,k;
int res;
int fa[100005],d[100005];
int find(int x){
if(fa[x]!=x){
int f=fa[x];
fa[x]=find(fa[x]);
d[x]=(d[x]+d[f])%3;
}
return fa[x];
}
void merge(int x,int y,int w){
int fx=find(x), fy=find(y);
if(fx==fy){
if(((d[x]-d[y]+3)%3)!=w-1) res++;
}
else{
fa[fx]=fy;
d[fx]=(d[y]-d[x]+w+2)%3;
}
}
int main()
{
cin>>N>>k;
for(int i=1;i<=N;i++) fa[i]=i;
while(k--){
int w,x,y; cin>>w>>x>>y;
if(x>N||y>N||x==y&&w==2) res++;
else merge(x,y,w);
}
cout<<res;
}
2. 树状数组
2.1. 单点修改&区间查询
模板题 P3374 代码:
CPP#include<bits/stdc++.h>
using namespace std;
int N,M;
int a[500005];
inline int lowbit(int x){
return x&-x;
}
class Tree{
private:
int t[500005];
void update(int p,int x){
while(p<=N){
t[p]+=x;
p+=lowbit(p);
}
}
int get_sum(int p){
int res=0;
while(p){
res+=t[p];
p-=lowbit(p);
}
return res;
}
public:
void modify(int p,int x){
update(p,x);
}
int query(int l,int r){
return get_sum(r)-get_sum(l-1);
}
}T;
int main()
{
cin>>N>>M;
for(int i=1;i<=N;i++){
cin>>a[i];
T.modify(i,a[i]);
}
while(M--){
int op,x,y; cin>>op>>x>>y;
if(op==1) T.modify(x,y);
if(op==2) cout<<T.query(x,y)<<'\n';
}
}
2.2. 区间修改&单点查询
维护差分数组即可,略。
2.3 区间修改&区间查询
维护两个差分数组。像这样的操作通常用线段树维护,代码略。
3 线段树
直接给出超级线段树代码:
CPP#include<bits/stdc++.h>
using namespace std;
int N;
int a[100005];
namespace SGT{
struct Node{
int l,r;
int dat;
int maxn,minn;
int add,mul;
int ass;
};
class Tree{
#define int long long
#define ls i<<1
#define rs i<<1|1
private:
Node t[400005];
void push_up(int i){
t[i].dat=t[ls].dat+t[rs].dat;
t[i].maxn=max(t[ls].maxn,t[rs].maxn);
t[i].minn=max(t[ls].minn,t[rs].minn);
}
void tag_add(int i,int x){
t[i].add+=x;
t[i].dat+=(t[i].r-t[i].l+1)*x;
t[i].maxn+=x, t[i].minn+=x;
}
void tag_mul(int i,int x){
t[i].mul*=x, t[i].add*=x;
t[i].dat*=x, t[i].maxn*=x, t[i].minn*=x;
}
void tag_ass(int i,int x){
t[i].add=0, t[i].mul=1;
t[i].ass=x;
t[i].dat=(t[i].r-t[i].l+1)*x;
t[i].maxn=t[i].minn=x;
}
void push_down(int i){
if(t[i].ass!=-1){
tag_ass(ls,t[i].ass);
tag_ass(rs,t[i].ass);
t[i].ass=-1;
}
if(t[i].mul){
tag_mul(ls,t[i].mul);
tag_mul(rs,t[i].mul);
t[i].mul=1;
}
if(t[i].add){
tag_add(ls,t[i].add);
tag_add(rs,t[i].add);
t[i].add=0;
}
}
void build(int i,int L,int R,int a[]){
t[i].l=t[i].r=0, t[i].add=0, t[i].mul=1;
if(L<=t[i].l&&t[i].r<=R){
t[i].dat=t[i].maxn=t[i].minn=a[L];
return ;
}
int Mid=L+R>>1;
build(ls,L,Mid,a);
build(rs,Mid+1,R,a);
push_up(i);
}
void update_add(int i,int L,int R,int x){
if(L<=t[i].l&&t[i].r<=R){
tag_add(i,x);
return ;
}
int mid=t[i].l+t[i].r>>1;
push_down(i);
if(L<=mid) update_add(ls,L,R,x);
if(mid<R) update_add(rs,L,R,x);
push_up(i);
}
void update_mul(int i,int L,int R,int x){
if(L<=t[i].l&&t[i].r<=R){
tag_mul(i,x);
return ;
}
int mid=t[i].l+t[i].r>>1;
push_down(i);
if(L<=mid) update_mul(ls,L,R,x);
if(mid<R) update_mul(rs,L,R,x);
push_up(i);
}
void update_ass(int i,int L,int R,int x){
if(L<=t[i].l&&t[i].r<=R){
tag_ass(i,x);
return ;
}
int mid=t[i].l+t[i].r>>1;
push_down(i);
if(L<=mid) update_ass(ls,L,R,x);
if(mid<R) update_ass(rs,L,R,x);
push_up(i);
}
int get_sum(int i,int L,int R){
if(L<=t[i].l&&t[i].r<=R) return t[i].dat;
int mid=t[i].l+t[i].r>>1, res=0;
push_down(i);
if(L<=mid) res+=get_sum(ls,L,R);
if(mid<R) res+=get_sum(rs,L,R);
}
int get_max(int i,int L,int R){
if(L<=t[i].l&&t[i].r<=R) return t[i].maxn;
int mid=t[i].l+t[i].r>>1, res=0;
push_down(i);
if(L<=mid) res=max(res,get_max(ls,L,R));
if(mid<R) res=max(res,get_max(rs,L,R));
}
int get_min(int i,int L,int R){
if(L<=t[i].l&&t[i].r<=R) return t[i].minn;
int mid=t[i].l+t[i].r>>1, res=1e18;
push_down(i);
if(L<=mid) res=min(res,get_min(ls,L,R));
if(mid<R) res=min(res,get_min(rs,L,R));
}
public:
void init(int a[]){
build(1,1,N,a);
}
void modify(int op,int L,int R,int x){
if(op==1) update_add(1,L,R,x);
if(op==2) update_mul(1,L,R,x);
if(op==3) update_ass(1,L,R,x);
}
int query_sum(int L,int R){
return get_sum(1,L,R);
}
int query_max(int L,int R){
return get_max(1,L,R);
}
int query_min(int L,int R){
return get_min(1,L,R);
}
#undef int
#undef ls
#undef rs
};
}
int main()
{
}
4. 分块
4.1 基础分块
数列分块入门 1 代码:
CPP#include<bits/stdc++.h>
#define int long long
using namespace std;
int N;
int a[300005];
int tag[1005];
int B[300005],L[1005],R[1005];
void build(){
int Bl=sqrt(N);
int l=1,r=Bl,cnt=1;
while(true){
bool flag=0;
if(r>N) r=N, flag=1;
L[cnt]=l, R[cnt]=r;
for(int i=l;i<=r;i++) B[i]=cnt;
if(flag) break;
l+=Bl, r+=Bl, cnt++;
}
}
void modify(int l,int r,int c){
int lb=B[l], rb=B[r];
if(lb==rb){
for(int i=l;i<=r;i++) a[i]+=c;
return ;
}
for(int i=l;i<=R[lb];i++) a[i]+=c;
for(int i=lb+1;i<=rb-1;i++) tag[i]+=c;
for(int i=L[rb];i<=r;i++) a[i]+=c;
}
int query(int p){
int b=B[p];
return a[p]+tag[b];
}
signed main()
{
cin>>N;
build();
for(int i=1;i<=N;i++) cin>>a[i];
for(int i=1,op,l,r,c;i<=N;i++){
cin>>op>>l>>r>>c;
if(op==0) modify(l,r,c);
else cout<<query(r)<<'\n';
}
}
数列分块入门 2 代码:
CPP#include<bits/stdc++.h>
#define int long long
using namespace std;
int N,q,Bl;
int B[1000005],L[30005],R[30005],tag[30005];
int a[1000005],b[1000005];
void build(){
Bl=sqrt(N);
int l=1-Bl,r=0,cnt=0;
while(true){
l+=Bl, r+=Bl, cnt++;
if(r>N) r=N;
L[cnt]=l, R[cnt]=r;
for(int i=l;i<=r;i++) B[i]=cnt;
if(r==N) break;
}
for(int i=1;i<=N;i++) cin>>a[i];
for(int i=1;i<=B[N];i++){
for(int j=L[i];j<=R[i];j++) b[j]=a[j];
sort(b+L[i],b+R[i]+1);
}
}
void modify(int l,int r,int c){
int lb=B[l],rb=B[r];
if(lb==rb){
for(int i=l;i<=r;i++) a[i]+=c;
for(int i=L[lb];i<=R[lb];i++) b[i]=a[i];
sort(b+L[lb],b+R[lb]+1);
return ;
}
for(int i=l;i<=R[lb];i++) a[i]+=c;
for(int i=L[lb];i<=R[lb];i++) b[i]=a[i];
sort(b+L[lb],b+R[lb]+1);
for(int i=lb+1;i<=rb-1;i++) tag[i]+=c;
for(int i=L[rb];i<=r;i++) a[i]+=c;
for(int i=L[rb];i<=R[rb];i++) b[i]=a[i];
sort(b+L[rb],b+R[rb]+1);
}
int query(int l,int r,int c){
int lb=B[l],rb=B[r],res=0;
if(lb==rb){
for(int i=l;i<=r;i++)
if(a[i]+tag[lb]<c) res++;
return res;
}
for(int i=l;i<=R[lb];i++)
if(a[i]+tag[lb]<c) res++;
for(int i=L[rb];i<=r;i++)
if(a[i]+tag[rb]<c) res++;
for(int i=lb+1;i<=rb-1;i++){
int p=c-tag[i];
res+=lower_bound(b+L[i],b+R[i]+1,p)-(b+L[i]);
}
return res;
}
signed main()
{
cin>>N;
build();
for(int i=1;i<=N;i++){
int opt,l,r,c;
cin>>opt>>l>>r>>c;
if(!opt) modify(l,r,c);
else cout<<query(l,r,c*c)<<'\n';
}
}
4.2 莫队
模板题 P2709 代码:
CPP#include<bits/stdc++.h>
#define int long long
using namespace std;
int N,M,k,B;
int a[50005],b[50005];
int bel[50005],res[50005];
struct Q{
int l,r;
int id,ans;
}q[50005];
vector<Q> G[505];
bool cmp1(Q a,Q b){ return a.r<b.r; }
bool cmp2(Q a,Q b){ return a.id<b.id; }
signed main()
{
cin>>N>>M>>k; B=sqrt(N);
for(int i=1;i<=N;i++){
cin>>a[i];
bel[i]=(i+B-1)/B;
}
for(int i=1;i<=M;i++){
cin>>q[i].l>>q[i].r;
q[i].id=i;
G[bel[q[i].l]].push_back(q[i]);
}
for(int i=1;i<=2*B;i++)
sort(G[i].begin(),G[i].end(),cmp1);
for(int i=1;i<=2*B;i++){
if(G[i].empty()) continue;
memset(b,0,sizeof b);
int lp=G[i][0].l,rp=G[i][0].r,ans=0;
for(int j=lp;j<=rp;j++){
ans+=2*b[a[j]]+1;
b[a[j]]++;
}
res[G[i][0].id]=ans;
for(int j=1;j<G[i].size();j++){
while(rp<G[i][j].r){
ans+=2*b[a[++rp]]+1;
b[a[rp]]++;
}
while(lp<G[i][j].l){
ans-=2*b[a[lp]]-1;
b[a[lp]]--; lp++;
}
while(lp>G[i][j].l){
ans+=2*b[a[--lp]]+1;
b[a[lp]]++;
}
res[G[i][j].id]=ans;
}
}
for(int i=1;i<=M;i++) cout<<res[i]<<'\n';
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...