L2-数据结构基础-第07课-动态数组
原创 葫芦老师 云帆优培 2020-12-19 13:34
L2-数据结构基础-第07课-动态数组
变长数组
cin >> n;
int a[n];
上面的代码, 严格说来, c++ 标准上是不支持的, 尽管大部分编译器能编译过.
c++ 是 c 的超集, 但是却不支持变长数组, 是因为c++ 提供了更强大的动态数组 vector.
在信息学竞赛中,有些题目需要定义很大的数组,这样会出现“超出内存限制"的错误。比如,如果一个图的顶点太多,使用邻接矩阵就会超出内存限制,使用指针实现邻接表又很容易出错,而使用vector实现简洁方便,还可以节省存储空间。
vector
vector是动态数组, 可以容纳许多类型的数据,因此也被称为容器
vector头文件
#include <vector>
vector初始化
template <class T>
这里的尖括号, 不是大于号, 小于号, 这里面放的是 「类型形式参数」 . 这个尖括号要特别注意加空格, 否则可能会变成 >> 或者 << .
比如:
vector <vector<int> > mp // 两个 > > 之间必须有空格.
vector <int> a; //生成一个名为a,保存int型对象的vector
vector <string> b; //生成一个名为b,保存string对象的vector
vector <vector<int> > mp; //生成一个保存vector型变量的vector
方式1 给定大小
vector<int> a(10);
//定义具有10个整型元素的向量(尖括号为元素类型名,它可以是任何合法的数据类型),不具有初值,其值不确定
方式2 给定大小和初始值
vector<int> a(10, 1);
//定义具有10个整型元素的向量,且给出的每个元素初值为1
方式3 用向量/数组初始化
vector<int> a(b);
//用向量b给向量a赋值,a的值完全等价于b的值
vector<int> a(b.begin(), b.begin+3);
//将向量b中从0-2(共三个)的元素赋值给a,a的类型为int型
int b[7]={1,2,3,4,5,6,7};
vector<int> a(b, b+7);
//从数组中获得初值
关于数组和向量的不同和相似之处如下表所示
vector是动态数组,在堆中分配内存
vector与数组一样,都是分配的一段连续的内存空间,并且起始地址不变,因此能够很好支持随机存取,复杂度为 O(1) 。但是在头部或者中间进行插入和删除的时候会涉及元素的移动,即内存块的拷贝,所以在中间插入或者删除元素的复杂度为 O(n)对最后元素操作最快(在后面添加删除元素最快),此时一般不需要移动内存。
vector对象的几个重要操作
#include<vector>
vector<int> a, b;
//b为向量,将b的0-2个元素赋值给向量a
a.assign(b.begin(), b.begin() + 3);
//a含有4个值为2的元素
a.assign(4, 2);
//返回a的最后一个元素
a.back();
//返回a的第一个元素
a.front();
//返回a的第i元素,当且仅当a存在
a[i];
//清空a中的元素
a.clear();
//判断a是否为空,空则返回true,非空则返回false
a.empty();
//删除a向量的最后一个元素
a.pop_back();
//删除a中第一个(从第0个算起)到第二个元素,也就是说删除的元素从a.begin()+1算起(包括它)一直到a.begin()+3(不包括它)结束
a.erase(a.begin()+1, a.begin()+3);
//在a的最后一个向量后插入一个元素,其值为5
a.push_back(5);
//在a的第一个元素(从第0个算起)位置插入数值5,
a.insert(a.begin()+1, 5);
//在a的第一个元素(从第0个算起)位置插入3个数,其值都为5
a.insert(a.begin()+1, 3, 5);
//b为数组,在a的第一个元素(从第0个元素算起)的位置插入b的第三个元素到第5个元素(不包括b+6)
a.insert(a.begin()+1, b+3, b+6);
//返回a中元素的个数
a.size();
//返回a在内存中总共可以容纳的元素个数
a.capacity();
//将a的现有元素个数调整至10个,多则删,少则补,其值随机
a.resize(10);
//将a的现有元素个数调整至10个,多则删,少则补,其值为2
a.resize(10, 2);
//将a的容量扩充至100,
a.reserve(100);
//b为向量,将a中的元素和b中的元素整体交换
a.swap(b);
//b为向量,向量的比较操作还有 != >= > <= <
a==b;
顺序访问vector的几种方式
向向量a中添加元素
vector<int> a;
for(int i = 0; i < 10; i++) {
a.push_back(i);
}
通过下标方式获取
int a[6]={1,2,3,4,5,6};
vector<int> b(a,a+4);
for(int i = 0; i <= b.size() - 1; i++) {
cout << b[i] << endl;
}
通过迭代器方式读取
int a[6] = {1, 2, 3, 4, 5, 6};
vector<int> b(a, a+4);
vector<int>::iterator it;
for( it = b.begin(); it != b.end(); it++) {
cout<< *it << " ";
}
几种重要的算法
#include <algorithm>
//对a中的从a.begin()(包括它)到a.end()(不包括它)的元素进行从小到大排列
sort(a.begin(), a.end());
//对a中的从a.begin()(包括它)到a.end()(不包括它)的元素倒置,但不排列,如a中元素为1,3,2,4,倒置后为4,2,3,1
reverse(a.begin(), a.end());
//把a中的从a.begin()(包括它)到a.end()(不包括它)的元素复制到b中,从b.begin()+1的位置(包括它)开始复制,覆盖掉原有元素
copy(a.begin(), a.end(), b.begin()+1);
//在a中的从a.begin()(包括它)到a.end()(不包括它)的元素中查找10,若存在返回其在向量中的位置
find(a.begin(), a.end(), 10);
初始化二维vector
// 初始化一个 二维的matrix, 行M,列N,且值为0
vector<vector<int> > matrix(M, vector<int>(N));
//等价于下面的
vector<vector<int> > matrix(M);
for(int i=0;i<M;i++) {
matrix[i].resize(N);
}
//等价于下面的
vector< vector<int> > matrix;
matrix.resize(M);//M行
for(int i=0;i<matrix.size();i++){
matrix[i].resize(N);//每一行都是N列
}
// 初始化一个 二维的matrix, 行M,列N,且值自定义为data;
vector<vector<int>> matrix(M, vector<int>(N,data));
学会用大括号初始化二维数组
vector<vector<int>> matrix1{};
vector<vector<int>> matrix2{ {1}, {1, 1} };//学会用大括号初始化二维数组
matrix1.push_back({ 1, 2, 1 });//插入
初始化一个 二维vector,行M,列不固定
vector<vector> matrix(M); //M行,列数不固定
int col;
vector<int> temp;
for(int i = 0; i < M; i++){
cout << "please input the col of "<<i<<" row" << endl;
cin >> col;//确定第i行的列数
cout << i << " row has "<< col << " col " << " please input these " << endl;
for(int j = 0; j < col; j++{
int data;
cin >> data;
temp.push_back(data);
}
matrix[i] = temp;
temp.clear();
}
初始化一个二维vector, 行列都不固定
#include <iostream>
#include<vector>
using namespace std;
int main()
{
vector<vector<int>> matrix;//行,列数不固定
cout << "please input rows of matrix: " << endl;
int rows;
cin >> rows;
matrix.resize(rows);
int col;
vector<int> temp;
for (int i = 0; i < rows; i++) {
cout << "please input the cols of " << i << "th row" << endl;
cin >> col;//确定第i行的列数
cout << i << "th row has " << col << " cols," << "please input these" << endl;
for (int j = 0; j < col; j++){
int data;
cin >> data;
temp.push_back(data);
}
matrix[i] = temp;
temp.clear();
}
cout << "output matrix:" << endl;
for (int i = 0; i < matrix.size(); i++) {
for (int j = 0; j < matrix[i].size(); j++) {
cout << matrix[i][j] << " ";
}
cout << endl;
}
cout << endl;
return 0;
}
例题 寄包柜
P3613 【深基15.例2】寄包柜
题目描述
超市里有 个寄包柜。每个寄包柜格子数量不一,第 i 个寄包柜有 个格子,不过我们并不知道各个 的值。对于每个寄包柜,格子编号从 1 开始,一直到 。现在有 次操作:
1 i j k:在第 i 个柜子的第 j 个格子存入物品 。当 k=0 时说明清空该格子。
2 i j:查询第 i 个柜子的第 j 个格子中的物品是什么,保证查询的柜子有存过东西。
已知超市里共计不会超过 个寄包格子, 是确定然而未知的,但是保证一定不小于该柜子存物品请求的格子编号的最大值。当然也有可能某些寄包柜中一个格子都没有。
输入格式
第一行 2 个整数 n 和 q,寄包柜个数和询问次数。
接下来 q 个整数,表示一次操作。
输出格式
对于查询操作时,输出答案。
输入输出样例
输入
5 4
1 3 10000 114514
1 1 1 1
2 3 10000
2 1 1
输出
114514
1
分析
内存限制 125M, 可以存放int类型的个数是 125 * 1024 * 1024 / 4 = 32768000. 所以 个格子是能存下的.
但是 但是, 寄包柜有 个, 每个寄包柜有 格子. 如果定义一个二维数组, 就会需要 个 int 所以 MLE.
那就用vector吧.
我们仍然不知道, 每个柜子有多少格子, 所以需要记录两个量, 一个是格子编号, 一个是格子里的物品. 先找编号, 然后找到该编号下的物品.
参考答案
#include <iostream>
#include <vector>
using namespace std;
const int N = 100005;
vector <int> a[N];
vector <int> idx[N];
int n, q;
int main()
{
cin >> n >> q;
while(q--) {
int opt, i, j, k;
cin>>opt;
if(opt == 1) {
cin >> i >> j >> k;
idx[i].push_back(j);
a[i].push_back(k);
}
else {
cin >> i >> j;
for(int id = a[i].size() - 1; i >= 0; id--) {
if(idx[i][id] == j) {
cout<<a[i][id]<<endl;
break;
}
}
}
}
return 0;
}
例题 杂务
P1113 杂务
题目描述
John的农场在给奶牛挤奶前有很多杂务要完成,每一项杂务都需要一定的时间来完成它。比如:他们要将奶牛集合起来,将他们赶进牛棚,为奶牛清洗乳房以及一些其它工作。尽早将所有杂务完成是必要的,因为这样才有更多时间挤出更多的牛奶。当然,有些杂务必须在另一些杂务完成的情况下才能进行。比如:只有将奶牛赶进牛棚才能开始为它清洗乳房,还有在未给奶牛清洗乳房之前不能挤奶。我们把这些工作称为完成本项工作的准备工作。至少有一项杂务不要求有准备工作,这个可以最早着手完成的工作,标记为杂务1。John有需要完成的n个杂务的清单,并且这份清单是有一定顺序的,杂务k(k>1)的准备工作只可能在杂务1至k−1中。
写一个程序从1到n读入每个杂务的工作说明。计算出所有杂务都被完成的最短时间。当然互相没有关系的杂务可以同时工作,并且,你可以假定John的农场有足够多的工人来同时完成任意多项任务。
输入格式
第1行:一个整数n,必须完成的杂务的数目(3≤n≤10,000);
第2至(n+1)行:共有n行,每行有一些用1个空格隔开的整数,分别表示:
工作序号(1至n,在输入文件中是有序的);
完成工作所需要的时间len(1≤len≤100);
一些必须完成的准备工作,总数不超过100个,由一个数字0结束。有些杂务没有需要准备的工作只描述一个单独的0,整个输入文件中不会出现多余的空格。
输出格式
一个整数,表示完成所有杂务所需的最短时间。
输入输出样例
输入 #1复制
7
1 5 0
2 2 1 0
3 3 2 0
4 6 1 0
5 1 2 4 0
6 8 2 4 0
7 4 3 5 6 0
输出 #1复制
23
分析
这道题稍微复杂点. 建模的话, 每个任务有若干个前提任务, 这些任务都完成后, 该任务才能完成. 我们可以用vector
参考代码
#include <bits/stdc++.h>
using namespace std;
int n;
struct task {
int len;
vector<int> kl;
};
task k[10001];
int maxlen, len;
int main()
{
int i, p;
scanf("%d", &n);
for(i = 1; i <= n; i++) {
scanf("%d %d", &p, &k[i].len);
while(true) {
scanf("%d",&p);
if(p == 0)
break;
k[i].kl.push_back(p);
}
}
for(i = 1; i <= n; i++) {
len = 0;
for(int j = 0; j < k[i].kl.size(); j++) {
len = max(len, k[k[i].kl[j]].len);
}
k[i].len += len;
maxlen = max(k[i].len, maxlen);
}
printf("%d", maxlen);
return 0;
}