常用 STL 容器整合

常用 STL 容器整合

一、vector

vector 是 STL 提供的一种 内存连续,长度可变 的动态数组。

虽说动态数组,但 vector 的底层仍是定长数组。当数组大小不足时,vector 会倍增的申请、分配更多连续的空间。

定义

vector<int>h; 定义一个数据类型为 intvector h

需要头文件 #include<vector>

函数

  1. 元素访问

    • h.begin() 返回一个迭代器,指向 h 的第一个元素的位置。
    • h.end() 返回一个迭代器,指向 h 的最后一个元素的后一个位置。
    • h.front() 返回 h 中的第一个数。
    • h.back() 返回 h 中的最后一个数。
    • h[x] 返回 h 下标为 x 的元素。
  2. 元素修改

    • h.clear() 清空 h
    • h.push_back(int val)h 的末尾加入元素 val,均摊时间复杂度 \(O(1)\),最坏复杂度为 \(O(n)\)(倍增申请空间)。
    • h.pop_back() 删除 h 的最后一个元素,时间复杂度 \(O(1)\)
    • 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 操作。
  3. 元素个数

    • h.size() 返回 h 中的元素个数。
    • h.empty() 检查 h 是否为空。

应用

  • 邻接表存图
  1. 定义
struct edge{
	int to,val;
};
vector<edge>e[inf];
  1. 存图(有向图)
for(int i=1;i<=m;i++)
{
	int u=re(),v=re(),w=re();
	e[u].push_back((edge){v,w});
}
  1. 对边进行排序
for(int i=1;i<=n;i++)
	sort(h[i].begin(),h[i].end(),cmp);
  1. 遍历
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; 定义一个数据类型为 intstack h

需要头文件 #include<stack>

函数

  1. 元素访问

    • h.top() 返回 h 的栈顶元素。
  2. 元素修改

    • h.push(int val)h 的栈顶加入元素 val
    • h.pop() 弹出 h 的栈顶元素。
  3. 元素个数

    • 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; 定义一个数据类型为 intqueue h

需要头文件 #include<queue>

函数

  1. 元素访问

    • h.front() 返回 h 的队首元素。
    • h.back() 返回 h 的队尾元素。
  2. 元素修改

    • h.push(int val)h 的队尾加入元素 val
    • h.pop() 弹出 h 的队首元素。
  3. 元素个数

    • h.size() 返回 h 中的元素个数。
    • h.empty() 检查 h 是否为空。

stack 基本一致,只是多了一些。

特殊队列:双端队列 deque

deque<int>h; 定义一个数据类型为 intdeque h

需要头文件 #include<deque>

  1. 元素访问

    • h.front() 返回 h 的队首元素。
    • h.back() 返回 h 的队尾元素。
    • h[x] 返回 h 下标为 x 的元素。
  2. 元素修改

    • 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) 之间的元素,并返回指向下一个迭代器。
  3. 元素个数

    • h.size() 返回 h 中的元素个数。
    • h.empty() 检查 h 是否为空。

好像和 vector 差不多,而且好像比 vector 更高级。

但不用一次你绝对不知道 deque 的空间复杂度多大(血的教训)

特殊队列:优先队列 priority_queue

priority_queue 是 STL 提供的一种二叉堆,默认大根堆。

priority_queue<int>h; 定义一个数据类型为 intpriority_queue h

需要头文件 #include<queue>

  1. 元素访问

    • h.top() 返回 h 中的最大值。
  2. 元素修改

    • h.push(int val)h 中的加入元素 val,时间复杂度 \(O(\log n)\)
    • h.pop() 弹出 h 中的最大值,时间复杂度 \(O(\log n)\)
  3. 元素个数

    • h.size() 返回 h 中的元素个数。
    • h.empty() 检查 h 是否为空。

应用

  • BFS/SPFA

BFS

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;
		}
	}
}

SPFA

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; 定义一个数据类型为 intset T

需要头文件 #include<set>

函数

  1. 元素访问

    • 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()
  2. 元素修改

    • T.clear() 清空 T
    • T.insert(int val)T 中插入一个元素 val,并返回一个 pairfirst 为迭代器,表示插入位置,second 为布尔值,表示是否插入成功。
    • T.erase(int val) 删除所有与 val 相等的元素,并返回删除元素个数。
    • T.erase(iterator pos) 删除 pos 位置的元素,并返回指向下一个迭代器。
    • T.erase(iterator sta,iterator end) 删除位于 [sta,end) 之间的元素,并返回指向下一个迭代器。
  3. 元素个数

    • 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、值为 intmap T

需要头文件 #include<map>

函数

  1. 元素访问

    • 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()
  2. 元素修改

    • T.clear() 清空 T
    • T.insert(pair<long long,int> P)T 中插入一个键值对 P,并返回一个 pairfirst 为迭代器,表示插入位置,second 为布尔值,表示是否插入成功。
    • T.erase(long long val) 删除所有键为 val 的键值对,并返回删除元素个数。
    • T.erase(iterator pos) 删除 pos 位置的键值对,并返回指向下一个迭代器。
    • T.erase(iterator sta,iterator end) 删除位于 [sta,end) 之间的元素,并返回指向下一个迭代器。
  3. 元素个数

    • T.size() 返回 T 中的元素个数。
    • T.empty() 检查 T 是否为空。

应用

当时练习堆排序和归并排序的受害者就是它

当值域很大(\(a_i\le10^9\))或者不能作为下标(如 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,具体可以查阅这篇博客

posted @ 2022-08-26 15:51  Zvelig1205  阅读(2485)  评论(0)    收藏  举报