手撕大厂笔试之C++STL容器全家桶

目录

个人介绍

前言

map——映射

         本质

定义方法

时间复杂度

   例题1:

   例题2:

      代码:

      总结: 

vector容器

基本操作

例题1

    代码

    分析

例题2

    代码

pair的用法

基本介绍

用法介绍

例题

    分析

    代码

    总结

优先队列

简介

定义方式

操作方法

应用:迪杰斯特拉最短路

set容器

基本功能

操作方法

操作方法1:查找

例题:

   代码:

操作方法二:找出集合不同的元素的个数

题目1

    分析

    代码

题目2

    分析

    代码 

操作方法三:

例题

    分析

    代码

操作方法四

例题

    分析

    代码


个人介绍

各位CSDN的友友们,大家好,我是小熙,一个算法小透明,希望在CSDN的大舞台可以和大家一起取得进步(^_^)

个人现状:上个学期在学校的ACM小组学了一个学期的算法,但是世界很大,我的梦想很远,我希望在我年轻的时候可以走出国门,到世界的其他地方看一看,再加上我的算法功底可能真的不太足以支持我在ACM比赛上做出成绩。所以我选择逐渐淡出ACM小组,并把之前的知识整理成博客供大家分享。

学习算法的小方法:知道算法的基本原理——>知道代码实现——>总结出代码模板并熟练于心——>基本模板题——>变式思维题

前言

每当讲到C++STL容器啥的,你是不是总会看到下面的玩意

怎么样,是不是头大了

 

但下面,我们就从题目的角度入手,来看看各大容器的哪些用法是刷题有用的,哪些是刷题没用的,至于那些刷题没用的,不会也没关系。引用ACM区域赛金牌大神刁鹏杰学长的话——我们是做题家,不是理论家。

map——映射

本质

表示的是一一映射的关系。

定义方法

map定义方法:

map<key,value> a;

(1)第一个关键字可以理解为关键字key,每个关键字在map中只出现一次,第二个可以理解为关键字的值value。

(2)key和value可以是任何类型,可以是int,double,string。比如map<int,int>a;map<string,int>a;

时间复杂度

单次维护和查询的时间复杂度为O(logn)非常优秀

例题1:

map可以用来计数,map<int,int>book通常是桶排序的数组。book[2]就表示的是数组中2出现的次数。

例:给定一个数组a,它的大小长度为n,数组的里面的数据为a1,a2,a3........an。输入一个数据k,判断k在数组中出现的次数。

样例:

5

2 3 5 2 4

输出:

2

#include<bits/stdc++.h>
using namespace std;
map<int,int>book;
int n;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int temp;
		cin>>temp;
		book[temp]++;
	}
	int k;
	cin>>k;
	cout<<book[k]<<endl;
	return 0;
}

 总结:桶计数是map一个功能之一

例题2:

P5266 【深基17.例6】学籍管理 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

代码:

#include<bits/stdc++.h>
using namespace std;
map<string,int> book;
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int x;//x表示的命令
		cin>>x;
		if(x==1)//表示的命令1
		{
		  string t;
		  int y;
		  cin>>t>>y;
		  book[t]=y;	
		  cout<<"OK"<<endl;
		}
		
		if(x==2)//表示的命令2
		{
		   string t;
		   cin>>t;
		   if(!book.count(t)) cout<<"Not found"<<endl;
		   else
		   cout<<book[t]<<endl;	
		}
		
		if(x==3)//表示的命令3
		{
		   string t;
		   cin>>t;
		   if(!book.count(t)) cout<<"Not found"<<endl;	
		   else cout<<"Deleted successfully"<<endl;
		   book.erase(t);
		}	
		
		if(x==4)//表示的是命令4
		{
			cout<<book.size()<<endl;
		}
		
    }
}

总结: 

(1):这个题将map的一一映射的特性体现的淋漓尽致。

(2):以map<key,value>book为例,book.size()表示的是映射容器book中的元素的个数

(3):以map<key,value>book为例,book.count(key)表示查找关键字key的值是否存在

(4):以map<key,value>book为例,book.earse(key)表示清空关键字key的值

vector容器

基本操作

定义:例如vector<int>a,int还可以是double,long long,pair等数据类型

操作:

(1):以vector<int>a为例子,a.push_back(temp)表示向动态数组里面新加入值为temp的元素

(2):以vector<int>a为例子,a.size( )计算动态数组a中的元素的个数

(3):以vector<int>a为例子,!a.empty()查看动态数组a是否为空

例题1

P2671 [NOIP2015 普及组] 求和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

 

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=10007;
const ll N=1e5+5;
struct node{
	ll num;
	ll color;
}k[N]; 
vector<ll>e1[N];
vector<ll>e2[N];
int main()
{
	ll n,m;cin>>n>>m;
	ll sum=0;
	for(int i=1;i<=n;i++)
	{
		cin>>k[i].num; 
	}
	for(int i=1;i<=n;i++)
	{
		cin>>k[i].color;
		if(i&1) e1[k[i].color].push_back(i);
		else e2[k[i].color].push_back(i);  
	}
	
	for(int i=1;i<=m;i++)
	{
		int len=e1[i].size();
		ll a,b;
		for(int l=0;l<len-1;l++)
		{
			for(int r=l+1;r<len;r++)
			{
				a=e1[i][l];
				b=e1[i][r];
				sum+=(a+b)*(k[a].num+k[b].num);
				sum%=mod;
				}
		}
		
		len=e2[i].size();
		for(int l=0;l<len-1;l++)
		{
			for(int r=l+1;r<len;r++)
			{
				a=e2[i][l];
				b=e2[i][r];
				sum+=(a+b)*(k[a].num+k[b].num);
				sum%=mod;
			}
		}
		
	}
	cout<<sum;
}

分析

这道题比较考察数学分析,但也不是特别复杂。不难看出决定三元组变量结果的只有x,y。但是x与y的和是2z,是一个偶数,所以x与y的奇偶性一定要是相同的。然后利用vector进行分类计算就可以了。

例题2

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
vector<int>e[N];//e[i]表示第i个帖子的ID编号集
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		char temp[6];
		int x,y;
		cin>>temp>>x>>y;
		
		if(temp[0]=='A')
		{
			e[x].push_back(y);
		}
		if(temp[0]=='Q')
		{
			if(y>e[x].size()+1)
			{
				cout<<"-1"<<endl;
			}
			cout<<e[x][y-1]<<endl;
		}
	}
	return 0;
}

 总结:有的时候像例一这种数学分类的题和例二这种一对多(一个帖子有多个ID号码)的题目用下vector可能更方便。

pair的用法

基本介绍

个人理解:可以把pair<int,int>理解为数据类型,地位等同double,int,long long。pair<int,int>a[N]就是pair类型的数组。

引用元素:pair<int,int>a为例,a.first表示引用的是第一个元素,a.second表示引用的是是第二个元素。

关于排序:用STL sort排序pair类型的数组按照的是first元素排列的。

 用法介绍

类似结构体,不过只有两个元素,把这两个元素捆绑在一起。通常在把二维坐标(x,y)或者直线(斜率k和参数b)捆绑在一起当pair。

 例题

 分析

前面在用法介绍那里讲了,看到直线就要想到捆绑"k"和“b”用pair

 代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int cnt;
struct node{
	int x;
	int y;
}e[N];
typedef pair<double,double> pdd;
set<pdd>a;//存储直线
int main()
{
	for(int i=0;i<=19;i++)
	{
		for(int j=0;j<=20;j++)
		{
			e[++cnt].x=i;
			e[cnt].y=j;
		}
	}
    cout<<cnt<<endl;
	for(int i=1;i<=cnt-1;i++)
	{
		for(int j=i+1;j<=cnt;j++)
		{
			if((e[i].x==e[j].x)||(e[i].y==e[j].y)) continue;
			
		     double k1=(1.0*(e[j].y-e[i].y))/(e[j].x-e[i].x);//计算直线k
			 double b1=(e[i].y*e[j].x-e[i].x*e[j].y)*1.0/(e[j].x-e[i].x);//计算b
			 a.insert({k1,b1});   		
			
		}
	}
	cout<<a.size()+41<<endl;
	 
	return 0;
}

总结

(1)关于两点的直线的结论

(2)直线相关的问题好要特判平行的情况。

优先队列

简介

与队列的不同是它可以自定义数据的优先级,让优先级高的排在前面,优先出队。

定义方式

priority_queue<数据类型,容器类型,比较方式>

例如:priority_queue<int,vector<int>,greater<int>> q是大根堆又称升序队列 

           priority_queue<int,vector<int>,less<int>> q是小根堆又称降序队列 

操作方法

*top:访问的是队头的元素

*size:队列中元素的个数

*push:把元素加到优先队列里面去

*empty:优先队列是否为空

应用:迪杰斯特拉最短路

我水平真的非常有限,只知道优先队列它有这种用法了,抱歉,各位读者。迪杰斯特拉会在图论专题中介绍,先添加一个模板放在这里。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N=2e5+5;
typedef long long ll;
int n,m,s;//s表示的是坐标源点
ll dis[N];
bool vis[N];
vector<pii>e[N];

priority_queue<pii,vector<pii>,greater<pii>> q;
void dijsktra()
{

        memset(dis,0x3f,sizeof(dis));
        dis[s]=0;
        memset(vis,0,sizeof(vis));
        //vis和dis初始化,优先队列
        
        q.push(make_pair(0,s));
        //加入源点loo
        
        while(!q.empty())
        {
                int u=q.top().second;
                q.pop();
                if(vis[u]) continue;
                vis[u]=1;//表示已经入过一次队了
                for(int i=0;i<e[u].size();i++)
                {
                        int v=e[u][i].first,w=e[u][i].second;
                        if(dis[v]>dis[u]+w)
                        {
                                dis[v]=dis[u]+w;
                                q.push(make_pair(dis[v],v));
                        }
                }
        }
        //跑迪杰斯特拉
}

int main()
{
        cin>>n>>m>>s;
        for(int i=1;i<=m;i++)
        {
                int x,y,z;
                cin>>x>>y>>z;
                e[x].emplace_back(y,z);
        }
        dijsktra();
        
        for(int i=1;i<=n;i++) cout<<dis[i]<<" ";
        return 0;
}

set容器

基本功能

 拥有自动去重和排序的作用。

操作方法

 前言:set的操作方法太多了,一把列举出来的话容易让人云里雾里,就以题目的方式来讲述set的操作方法吧。

 操作方法一:查找

(1)以set<int>a为例子,a.count(k)表示set集合里面是否存在k这个数据。

(2)以set<int>a为例子,a.find(k)==a.end()表示set集合里面不存在k这个数据。反之表示存在k这个数据。

总结:以上两种操作方法的作用是查找。

例题:

给出一个长度大小为n的数组a,数组中的元素个数为a1,a2,a3.......an-1,an,请输入一个数字k,判断数组中是否有k这个数,有的话输入"OK"无的话输入“NO”

样例:

5

2 3 2 4 6 

4

输出

OK 

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
set<int>a;
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int t;
		cin>>t;
		a.insert(t);
	}
	int k;
	cin>>k;
	if(a.count(k)) cout<<"OK";
	else
	cout<<"NO"<<endl;
	
}
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
set<int>a;
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int t;
		cin>>t;
		a.insert(t);
	}
	int k;
	cin>>k;
	if(a.find(k)!=a.end()) cout<<"OK";
	else
	cout<<"NO"<<endl;
	
}

操作方法二:找出集合不同的元素的个数

以set<int>a为例子,a.size()表示的是集合a中不同元素的个数

题目1

P3370 【模板】字符串哈希 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

分析

看到不同二字,立马想到set,然后没了。

代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n;cin>>n;
	string p;
	set<string>a;
	for(int i=1;i<=n;i++)
	{
		cin>>p;
		a.insert(p);
	}
	cout<<a.size();
}

 题目2

分析

转化为斜率k来分析,求斜率k不同的数量 

代码 

#include<bits/stdc++.h>
using namespace std;
set<double>a;
int cnt;
int main()
{
	int n;cin>>n;
	double x0,y0;
	cin>>x0>>y0;
	for(int i=1;i<=n;i++)
	{
		double x1,y1;
		cin>>x1>>y1;
		if(x1==x0) 
		{
		   cnt=1;
		   continue;	
		}
		double temp=(y1-y0)/(x1-x0);
		a.insert(temp);
	}
	cout<<a.size()+cnt;
	return 0;
}

 操作方法三:

(1):auto定义stl的迭代器,在遍历stl的时候需要用迭代器来遍历。这里的迭代器可以把他理解为指针。

(2):a.lower_bound(x)返回的是迭代器,在写的时候经常写成auto it=lower_bound(x)

(3):以set<int>a为例,a.erase(x)表示的是删除操作,删除set集合中的x值

 例题

P5250 【深基17.例5】木材仓库 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

分析

看到“保障没有两个木材长度相同”,直接set

代码

#include<bits/stdc++.h>
using namespace std;
set<int>a;
int n;
int main()
{
	int n;cin>>n;
	while(n--)
	{
		int x,y;
		cin>>x>>y;//x表示的是命令,1的话就是进货,2的话就是出货
		
		if(x==1)
		{
			if(!a.count(y))
			{
				a.insert(y);
			}
			else
			{
				cout<<"Already Exist"<<endl;
			}
		}
		
		if(x==2)
		{
			if(a.size()==0) 
			{
			cout<<"Empty"<<endl;
			continue;
		    }
			if(a.count(y)){
				cout<<y<<endl;
				a.erase(y);
			}else
			{
				auto it=a.lower_bound(y);//a.lower_bound返回的是迭代器,即指针
				int k1=*it;
				if(it!=a.begin()) it--;
				
				int k2=*it;
				
				if(abs(k1-y)<abs(k2-y)){
					cout<<k1<<endl;
					a.erase(k1);
				}else{
					cout<<k2<<endl;
					a.erase(k2);
				}
			}		
		}
	}
}

操作方法四

迭代器iterator,遍历容器的作用。

通常写法:for(set<int,int>::iterator it=a.begin():it!=a.end();it++)

例题

 分析

本题虽然与集合相关,但是把set中的元素提取到数组里面的过程用了上面讲的迭代器的遍历

代码

#include<bits/stdc++.h>
using namespace std;
typedef pair<double,double> pdd;
set<pdd>a;//先用set容器装直线,对直线进行去重操作
const int N=1e3+10;
pdd b[N];//以数组的方式存储去重之后的直线
int n;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		double x,y;
		cin>>x>>y;
		a.insert({x,y});
	}
	int cnt=0;
	
	for(set<pdd>::iterator it=a.begin();it!=a.end();it++,++cnt)//这是以迭代器的方式遍历set容器里面的每一个元素,背下来就可以了,反正搞明白也意义不大
	{
		double x=it->first;double y=it->second;
		pdd temp;
		temp.first=x;
		temp.second=y;
		b[cnt]=temp;
	}//这一个部分的作用遍历set里面的每一个元素然后把他提取到数组b里面去
	
	int ans=2;
	
	//这一个步骤就是枚举直线了,第一层循环枚举2~i的每一个直线,第二层循环是第i条直线之前二代每一条直线。
	
	for(int i=2;i<=cnt;i++)
	{
		set<double>t;//set<double>t用来装第i条加入的直线与之前加入的直线的交点
		for(int j=1;j<i;j++)
		{
			double a1=b[i].first;
			double a2=b[j].first;
			double b1=b[i].second;
			double b2=b[j].second;
			if(a1!=a2)//不平行的话就求交点
			{
				double temp=(b2-b1)/(a1-a2);
				t.insert(temp);
			}
		}
		ans+=t.size()+1;//新增平面数量等于交点数量加一
	}
	
	cout<<ans;
	return 0;
}
  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值