一,数据结构回顾
(1)空间相关
空间:
栈空间:函数用的空间
堆空间:程序员手动控制的空间
段空间:全局变量,static变量的空间,以及代码和各种指令
经典运行错误报错:segmentation fault段错误
栈空间与构造函数:拷贝构造和赋值构造
cpp
//构造函数
struct Person {
Person(int a) : age(a) {}
Person(const Person &p) {
age = p.age;
}
int age;
};
//调用
Person p1(18);
Person p2(p1);
堆空间:使用new等方式申请,堆空间没有自动回收-->内存泄漏即没有正确的回收堆空间的内容,导致占用空间越来越多,最后内存不够用,程序崩溃,解决办法:析构函数
段空间:编译阶段需要提前计算好段空间的大小,所以全局变量这么写会编译错误
cpp
int n=10,a[N];
(2)c++模板:template
template 是 C++ 实现泛型编程的语法机制,可基于类型参数生成适配任意数据类型的函数或类。
cpp
template<typename T>//类型任意
T max(T a,T b){
if(a>b) return a;
return b;
}
可进行swap用于清空任意类型的STL容器,例如stack,priority_queue等等
二,容器介绍
(1)vector动态数组
直接举例
cpp
vector<int>a;//数组长度为0
vector<int>b(10);//数组长度为10,每个元素是0
vector<int>c(10,7);//数组长度为10,每个元素是7
vector套vector构建二维数组
以定义一个10行3列的二维数组为例
cpp
vecotr<vector<int> > a(10,vector<int>(3);
更常见的二维写法有(链式前向星)
cpp
vector<int>a[15];
也可以模拟栈
cpp
b.push_back(x)
b.pop_back()
b.back()
(2)双端队列
C++ 双端队列 deque 是头尾都能快速增删元素的动态数组容器。
cpp
deque<int> dq;
dq.push_front();
dq.push_back();
dq.pop_front();
dp.pop_back();
//注意:
//错误写法:if(dq.front()==3)
//正确写法:if(!dq.empty()&&dq.front()==3)
补充短路机制:
像0&&x和1||x这两条语句中的x都不会执行
(3)集合
C++ set 是内部自动升序排序、不允许存放重复元素的有序容器,底层红黑树实现,查找插入删除效率稳定。
插入的时候其实是二叉搜索树(其实是优化过后的红黑树),所以对元素有排序和去重
cpp
set<int> s;
s.insert(3);
s.erase(3);
遍历:
迭代器版
for(set<int>::itetator it=s.begin();it!=s.end();it++){
cout<<*it<<" ";
}
c++14及以上
for(int x:s) cout<<x<<" ";
(4)多重集
只排序不去重的set
C++ multiset 是自动升序排序、允许存放重复元素的有序容器,底层红黑树实现,增删查效率稳定
cpp
multiset<int> ms;
ms.erase(3);//删除所有的3
ms.erase(ms.lower_bound(3));//删除一个3
例题:包裹与数
有一个用于装整数的包裹,一开始包裹里没有数,接下来有 n 次操作,每一次操作包含三种类型。
1 x将一个整数 x 放入包裹
2 x从包裹中取出一个值为 x 的数(保证包裹中有值为 x 的数)
3询问包裹中所有数中最接近的一对数(即差的绝对值最小的两个数,并输出这对数的差的绝对值)对于每次查询操作,输出对应的结果。如果查询时包裹中整数个数小于 2 个,输出 2147483647
放入处理方法:在差值集中找到x临近两数差值并删除,加上x与两数差值,原值集中补x
取出处理方法:在差值集中找到x与两数差值并删除,加上x临近两数差值,原值集中补x
查询直接输出差值集第一个
注意删除方法不要删掉所有的,所以形式同上方代码第二种
代码如下
cpp
#include<bits/stdc++.h>
using namespace std;
multiset<int> s,diffs;
void add(int x){
multiset<int>::iterator it=s.lower_bound(x);
int num1=-1,num2=-1;
if(it!=s.end()){
num1=*it;
diffs.insert(num1-x);
}
if(it!=s.begin()){
it--;
num2=*it;
diffs.insert(x-num2);
}
if(num1!=-1&&num2!=-1){
it=diffs.lower_bound(num1-num2);
diffs.erase(it);
}
s.insert(x);
}
void del(int x){
multiset<int>::iterator it=s.lower_bound(x);
int num1=-1,num2=-1;
it++;
if(it!=s.end()){
num1=*it;
diffs.erase(diffs.lower_bound(num1-x));
}
it--;
if(it!=s.begin()){
it--;
num2=*it;
diffs.erase(diffs.lower_bound(x-num2));
it++;
}
if(num1!=-1&&num2!=-1){
diffs.insert(num1-num2);
}
s.erase(s.lower_bound(x));
}
int main(){
int n,op,x;
scanf("%d",&n);
while(n--){
scanf("%d",&op);
if(op==1){
scanf("%d",&x);
add(x);
}
else if(op==2){
scanf("%d",&x);
del(x);
}
else{
if(s.size()>=2){
printf("%d\n",*diffs.begin());
}
else{
printf("2147483647\n");
}
}
}
}
(5)映射
已经很熟悉了
C++ map 是键唯一、按键自动升序排序的键值对容器,底层红黑树实现,增删查找效率稳定
来个map套map的例题,这个题关键的理解:map是由许多个pair构成的
仓库里面堆积着很多的货物。小可放假归来,发现仓库里多了许多的货物。
小可作为仓库管理员,需要对新多出来的货物进行分类整理。但是这段时间多出来的货物太多了,你能帮小可整理一下吗?
第一行一个正整数T(1≤ T≤ 10),代表有T组数据。对于每组数据,第一行有一个正整数n,代表有n条入库记录。
接下来有n行入库记录。
每个入库记录由字符串a,b和一个正整数m(1≤ m≤ 100)组成,a是货物名,b是货物来源,m是数量。这两个字符串的长度都不长于80。
请输出合并整理后格式正确的货物统计表。格式为:
把货物按照来源地分类,来源地按照字典序排序输出。每个来源地都有下级分类。
每个来源地下级分类中,每一行输出三个空格,一个|,四个-,用来保持缩进。子分类中,将来源地是此来源地的货物按照字典序排序,每个货物后面有一个括号,里面写货物的总量。
显然需要使用map,其中嵌套操作不是很好想(c++14遍历好用)
cpp
#include<bits/stdc++.h>
using namespace std;
int T,n,t;
string a,b;
map<string,map<string,int> > m;
int main(){
cin>>T;
while(T--){
cin>>n;
while(n--){
cin>>a>>b>>t;
m[b][a]+=t;
for(pair<string,map<string,int> > mi:m){
cout<<mi.first<<"\n";
for(pair<string,int>it:mi.second){
cout<<" |----"<<it.first<<"("<<it.second<<")"<<endl;
}
}
m.clear();
if(T) cout<<"\n";
}
}
}
祝大家天天开心,明天继续