常用 STL 容器整合
常用 STL 容器整合
一、vector
vector 是 STL 提供的一种 内存连续,长度可变 的动态数组。
虽说动态数组,但 vector 的底层仍是定长数组。当数组大小不足时,vector 会倍增的申请、分配更多连续的空间。
定义
vector<int>h;
定义一个数据类型为 int
的 vector
h
。
需要头文件 #include<vector>
。
函数
-
元素访问
h.begin()
返回一个迭代器,指向h
的第一个元素的位置。h.end()
返回一个迭代器,指向h
的最后一个元素的后一个位置。h.front()
返回h
中的第一个数。h.back()
返回h
中的最后一个数。h[x]
返回h
下标为x
的元素。
-
元素修改
h.clear()
清空h
。h.push_back(int val)
在h
的末尾加入元素val
,均摊时间复杂度 ,最坏复杂度为 (倍增申请空间)。h.pop_back()
删除h
的最后一个元素,时间复杂度 。h.insert(iterator pos,int val)
在pos
位置之前插入一个元素val
,时间复杂度与插入位置到h.end()
的距离有关。h.erase(iterator pos)
删除pos
位置的元素,并返回指向下一个迭代器,时间复杂度同insert
操作。h.erase(iterator sta,iterator end)
删除位于[sta,end)
之间的元素,并返回指向下一个迭代器,时间复杂度同insert
操作。
-
元素个数
h.size()
返回h
中的元素个数。h.empty()
检查h
是否为空。
应用
- 邻接表存图
- 定义
struct edge{ int to,val; }; vector<edge>e[inf];
- 存图(有向图)
for(int i=1;i<=m;i++) { int u=re(),v=re(),w=re(); e[u].push_back((edge){v,w}); }
- 对边进行排序
for(int i=1;i<=n;i++) sort(h[i].begin(),h[i].end(),cmp);
- 遍历
void dfs(int now) { for(int i=0;i<(h[now].size());i++) { ... dfs(h[now][i]); ... } }
- 桶优化 dijkstra
有兴趣者可以看 这个,此处不再赘述。
memset(dis,127,sizeof(dis)); int now=s;dis[now]=0; while(1) { vis[now]=1; for(int i=fir[now];i;i=nex[i]) { int p=pos[i]; if(dis[p]>dis[now]+val[i]) { dis[p]=dis[now]+val[i]; T[dis[p]].push_back(p); maxn=max(maxn,dis[p]); } } last=now; for(int i=1;i<=maxn;i++) { if(T[i].size()) { if(vis[T[i][0]]==0)now=T[i][0]; last=i;T[i].erase(T[i].begin()); break; } } if(last==now)break; }
- 模拟平衡树
cin>>op>>k; if(op==4)cout<<h[k-1]<<endl; if(op==2)h.erase(lower_bound(h.begin(),h.end(),k)); if(op==1)h.insert(upper_bound(h.begin(),h.end(),k),k); if(op==6)cout(*upper_bound(h.begin(),h.end(),k))<<endl; if(op==5)cout<<(*--lower_bound(h.begin(),h.end(),k))<<endl; if(op==3)cout<<(lower_bound(h.begin(),h.end(),k)-h.begin()+1))<<endl;
不要在意 1,2,3,4 的顺序,个人感觉这样逐行递增比较好看
二、stack
stack 是 STL 提供的一种栈。
定义
stack<int>h;
定义一个数据类型为 int
的 stack
h
。
需要头文件 #include<stack>
。
函数
-
元素访问
h.top()
返回h
的栈顶元素。
-
元素修改
h.push(int val)
在h
的栈顶加入元素val
。h.pop()
弹出h
的栈顶元素。
-
元素个数
h.size()
返回h
中的元素个数。h.empty()
检查h
是否为空。
和 vector
基本一致,只是少了很多。
应用
#include<bits/stdc++.h> int sum[57],top,ans; char s[57]; int main() { std::cin>>s; int len=strlen(s); for(int i=0;i<len;i++) { if(s[i]=='.')continue; if(s[i]>'9'||s[i]<'0') { if(s[i]=='+')ans=sum[top-1]+sum[top]; if(s[i]=='-')ans=sum[top-1]-sum[top]; if(s[i]=='*')ans=sum[top-1]*sum[top]; if(s[i]=='/')ans=sum[top-1]/sum[top]; sum[--top]=ans; } else { sum[++top]=0; while(s[i]!='.') sum[top]=sum[top]*10+s[i]-48,i++; } } std::cout<<ans; return 0; }
三、queue
queue 是 STL 提供的一种队列。
定义
queue<int>h;
定义一个数据类型为 int
的 queue
h
。
需要头文件 #include<queue>
。
函数
-
元素访问
h.front()
返回h
的队首元素。h.back()
返回h
的队尾元素。
-
元素修改
h.push(int val)
在h
的队尾加入元素val
。h.pop()
弹出h
的队首元素。
-
元素个数
h.size()
返回h
中的元素个数。h.empty()
检查h
是否为空。
和 stack
基本一致,只是多了一些。
特殊队列:双端队列 deque
deque<int>h;
定义一个数据类型为 int
的 deque
h
。
需要头文件 #include<deque>
。
-
元素访问
h.front()
返回h
的队首元素。h.back()
返回h
的队尾元素。h[x]
返回h
下标为x
的元素。
-
元素修改
h.push_front(int val)
在h
的队尾加入元素val
。h.push_back(int val)
在h
的队首加入元素val
。h.pop_front()
弹出h
的队首元素。h.pop_back()
弹出h
的队尾元素。h.insert(iterator pos,int val)
在pos
位置之前插入一个元素val
。h.erase(iterator pos)
删除pos
位置的元素,并返回指向下一个迭代器。h.erase(iterator sta,iterator end)
删除位于[sta,end)
之间的元素,并返回指向下一个迭代器。
-
元素个数
h.size()
返回h
中的元素个数。h.empty()
检查h
是否为空。
好像和 vector
差不多,而且好像比 vector
更高级。
但不用一次你绝对不知道 deque
的空间复杂度多大(血的教训)
特殊队列:优先队列 priority_queue
priority_queue 是 STL 提供的一种二叉堆,默认大根堆。
priority_queue<int>h;
定义一个数据类型为 int
的 priority_queue
h
。
需要头文件 #include<queue>
。
-
元素访问
h.top()
返回h
中的最大值。
-
元素修改
h.push(int val)
在h
中的加入元素val
,时间复杂度 。h.pop()
弹出h
中的最大值,时间复杂度 。
-
元素个数
h.size()
返回h
中的元素个数。h.empty()
检查h
是否为空。
应用
- BFS/SPFA
h.push((node){x,y}); vis[x][y]=1; while(h.size()) { node now=h.front();h.pop(); for(int i=0;i<8;i++) { int xx=now.x+dx[i],yy=now.y+dy[i]; if(xx>0&&yy>0&&xx<=n&&yy<=m&&vis[xx][yy]==0) { h.push((node){xx,yy}); ans[xx][yy]=ans[now.x][now.y]+1; vis[xx][yy]=1; } } }
memset(dis,127,sizeof(dis)); dis[s]=0;vis[s]=1; h.push(s); while(h.size()) { int now=h.front();h.pop(); vis[now]=0; for(int i=fir[now];i;i=nex[i]) { int p=pos[i]; if(dis[p]>dis[now]+val[i]) { dis[p]=dis[now]+val[i]; if(vis[p])continue; vis[p]=1;h.push(p); } } }
- 堆优 dijkstra
memset(dis,127,sizeof(dis)); h.push(node(s,0)); dis[s]=0; while(h.size()) { int now=h.top().to;h.pop(); if(vis[now])continue; vis[now]=1; int len=e[now].size(); for(int i=0;i<len;i++) { int p=e[now][i].to; if(dis[p]>dis[now]+e[now][i].val) { dis[p]=dis[now]+e[now][i].val; h.push(node(p,dis[p])); } } }
不会 SPFA 和堆优 dij 的可以查看 此博客。
四、set
set 是 STL 提供的集合,能实现平衡树的部分操作,其内部是一颗红黑树(一种很高效的平衡树)。
set 不支持重复元素,若需要用到多个相同元素,可使用 multiset。
定义
set<int>T;
定义一个数据类型为 int
的 set
T
。
需要头文件 #include<set>
。
函数
-
元素访问
T.begin()
返回一个迭代器,指向T
的第一个元素的位置。T.end()
返回一个迭代器,指向T
的最后一个元素的后一个位置。T.rbegin()
返回一个逆向迭代器,指向T
的第一个元素的前一个位置。T.rend()
返回一个逆向迭代器,指向T
的最后一个元素的位置。T.count(int val)
返回T
中元素val
的个数。T.find(int val)
返回一个迭代器,指向元素val
,不存在则返回T.end()
。T.lower_buond(int val)
返回一个迭代器,指向T
中第一个不小于val
的元素位置,不存在则返回T.end()
。T.upper_buond(int val)
返回一个迭代器,指向T
中第一个大于val
的元素位置,不存在则返回T.end()
。
-
元素修改
T.clear()
清空T
。T.insert(int val)
在T
中插入一个元素val
,并返回一个pair
,first
为迭代器,表示插入位置,second
为布尔值,表示是否插入成功。T.erase(int val)
删除所有与val
相等的元素,并返回删除元素个数。T.erase(iterator pos)
删除pos
位置的元素,并返回指向下一个迭代器。T.erase(iterator sta,iterator end)
删除位于[sta,end)
之间的元素,并返回指向下一个迭代器。
-
元素个数
T.size()
返回T
中的元素个数。T.empty()
检查T
是否为空。
应用
- 代替平衡树
int op,w,v,ansb,ansv; struct Flower{ int b,val; bool operator <(const Flower &b)const { return val<b.val; } }; set<Flower>T; int main() { while(1) { op=re(); if(op==-1)break; if(op==1) { w=re(),v=re(); T.insert((Flower){w,v}); } if(op==2&&T.size())T.erase(--T.end()); if(op==3&&T.size())T.erase(T.begin()); } for(set<Flower>::iterator i=T.begin();i!=T.end();i++) ansb+=i->b,ansv+=i->val; wr(ansb),putchar(' '),wr(ansv),putchar('\n'); return 0; }
五、map
map 是 STL 提供的映射,由 键 对应 值 而构成键值对,其内部是一颗红黑树(一种很高效的平衡树)。
map 不支持重复键,若需要用到多个相同键,可使用 multimap。
定义
map<long long,int>T;
定义一个键为long long
、值为 int
的 map
T
。
需要头文件 #include<map>
。
函数
-
元素访问
T[long long val]
返回一个整数,即键val
所映射的值。T.begin()
返回一个迭代器,指向T
的第一个键值对的位置。T.end()
返回一个迭代器,指向T
的最后一个键值对的后一个位置。T.rbegin()
返回一个逆向迭代器,指向T
的第一个键值对的前一个位置。T.rend()
返回一个逆向迭代器,指向T
的最后一个键值对的位置。T.count(long long val)
返回T
中键为val
的个数。T.find(long long val)
返回一个迭代器,指向键为val
,若不存在返回T.end()
。T.lower_buond(long long val)
返回一个迭代器,指向T
中第一个键不小于val
的键值对位置,不存在则返回T.end()
。T.upper_buond(long long val)
返回一个迭代器,指向T
中第一个键大于val
的键值对位置,不存在则返回T.end()
。
-
元素修改
T.clear()
清空T
。T.insert(pair<long long,int> P)
在T
中插入一个键值对P
,并返回一个pair
,first
为迭代器,表示插入位置,second
为布尔值,表示是否插入成功。T.erase(long long val)
删除所有键为val
的键值对,并返回删除元素个数。T.erase(iterator pos)
删除pos
位置的键值对,并返回指向下一个迭代器。T.erase(iterator sta,iterator end)
删除位于[sta,end)
之间的元素,并返回指向下一个迭代器。
-
元素个数
T.size()
返回T
中的元素个数。T.empty()
检查T
是否为空。
应用
当时练习堆排序和归并排序的受害者就是它
当值域很大()或者不能作为下标(如 string)时,直接开数组进行堆排序,编译器不允许的,这时就可以利用 map。
int n; map<int,int>T; int main() { n=re(); for(int i=1;i<=n;i++) T[re()]++; for(auto i:T) for(int j=0;j<i.second;j++) wr(i.first),putchar(' '); return 0; }
- 不完全取代离散化
其实作用还是当桶,但有时候时间复杂度不是很优(毕竟 map 的操作大都是 log 复杂度的)
比如这个题
分块 +map 80分,因为有个 log
const int inf=1e5+7; int n,m,len,a[inf]; int bel[inf],L[400],R[400]; map<int,short>T[400]; void change(int x,int k) { T[bel[x]][a[x]]--; a[x]=k; T[bel[x]][a[x]]++; } int ask(int l,int r,int k) { int lin=bel[l],rin=bel[r],ans=0; if(lin==rin) { for(int i=l;i<=r;i++) if(a[i]==k)ans++; return ans; } for(int i=l;i<=R[lin];i++) if(a[i]==k)ans++; for(int i=L[rin];i<=r;i++) if(a[i]==k)ans++; for(int i=lin+1;i<rin;i++) ans+=T[i][k]; return ans; } int main() { n=re();m=re();len=sqrt(n); for(int i=1;i<=n;i++) a[i]=re(); for(int i=1;i<=len;i++) L[i]=R[i-1]+1,R[i]=i*len; R[len]=n; for(int i=1;i<=len;i++) for(int j=L[i];j<=R[i];j++) bel[j]=i,T[i][a[j]]++; for(int i=1;i<=m;i++) { char op[10]="";scanf("%s",op); int x=re(),y=re(); if(op[0]=='C')change(x,y); else wr(ask(x,y,re())),putchar('\n'); } return 0; }
分块 + 离散化 AC
const int inf=1e5+7; int n,m,len,a[inf]; int bok[inf<<1],cnt; struct lsh{ char op[10]; int le,ri,k; }Q[inf]; int bel[inf],L[400],R[400]; short T[400][inf<<1]; void change(int x,int k) { T[bel[x]][a[x]]--; a[x]=k; T[bel[x]][a[x]]++; } int ask(int l,int r,int k) { int lin=bel[l],rin=bel[r],ans=0; if(lin==rin) { for(int i=l;i<=r;i++) if(a[i]==k)ans++; return ans; } for(int i=l;i<=R[lin];i++) if(a[i]==k)ans++; for(int i=L[rin];i<=r;i++) if(a[i]==k)ans++; for(int i=lin+1;i<rin;i++) ans+=T[i][k]; return ans; } int main() { n=re();m=re();len=sqrt(n); for(int i=1;i<=n;i++) bok[i]=a[i]=re(); for(int i=1;i<=m;i++) { scanf("%s",Q[i].op); if(Q[i].op[0]=='C')Q[i].le=re(),Q[i].k=re(); else Q[i].le=re(),Q[i].ri=re(),Q[i].k=re(); bok[n+i]=Q[i].k; } sort(bok+1,bok+n+m+1); int num=unique(bok+1,bok+n+m+1)-bok-1; for(int i=1;i<=n;i++) a[i]=lower_bound(bok+1,bok+num+1,a[i])-bok; for(int i=1;i<=m;i++) Q[i].k=lower_bound(bok+1,bok+num+1,Q[i].k)-bok; for(int i=1;i<=len;i++) L[i]=R[i-1]+1,R[i]=i*len; R[len]=n; for(int i=1;i<=len;i++) for(int j=L[i];j<=R[i];j++) bel[j]=i,T[i][a[j]]++; for(int i=1;i<=m;i++) { if(Q[i].op[0]=='C')change(Q[i].le,Q[i].k); else wr(ask(Q[i].le,Q[i].ri,Q[i].k)),putchar('\n'); } return 0; }
有时候就会感觉离散化很麻烦,但又不得不用……
UPD in 2022.10.12
C++11 之后推出了 unordered_map,由 hash 实现。
和 map 功能相同,只是去掉了个 log。
但容易被 hack,具体可以查阅这篇博客。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具