当然也可以不看=>阅读我的文章前请务必先看此文章!
目录
阅读文章须知
为了得到良好的阅读体验,请具备稍微完备的C++语法知识
在文章中出现类似[1]的标志,作者会在文章最后进行解释说明
注:只看蓝色加粗字加速阅读,红色加粗为误区或易错
引言
不管是在洛谷、LeetCode或者在一本通刷题时,难免去看一些巨佬的题解,然后在茫茫题解中仔细寻找,诶!这不和我的算法一模一样吗!一看代码,到处都是用的自带数据结构,这你就看不懂了,“啊这难道不能自己手写吗”,以至于看不懂他的代码,最后郁郁而放弃,这篇文章本来是要把三大数据结构,vector,stack和queue全部讲完的,但是发现一个vector就耗费了我大量的时间,那就只能单独发了,保证看完就会用,妈妈再也不用担心我看不懂巨佬的代码了,下次还可以拿这找小白装13
vector与数组
很多人觉得vector太麻烦了,还不如数组好用,感觉这俩基本上没区别,都是存数据的。
但是,vector可以实时添加数据,并且随着添加数据,它所占用的空间才会变大,也就是说,你往vector里放了几个数据,这个vector就开多大。相比之下,数组就不一样了,数组是直接预留出了大量的内存[1],不管你往里面放了多少的元素,它所占用的内存是一定的。而且,vector也同样可以预留空间,简单来说,数组能做的,vector也能做,vector还能做一些数组做不到的事
个人认为最好用的一个用法是:vector可以直接用“=”号将整个“数组”赋值
vector的创建
vector<int> a;
表示创建了一个名字为a的vector,vector中的元素类型是int,元素类型是可以更改的,比如下面这个
struct node
{
int x, y, z;
};
vector<node> a;
vector<pair<int, string>> b;
以上代码中,vector a的元素类型是一个名为node的结构体,类似于node a[105],一样可以用a[i].x等来引用,而vector b的元素类型是pair<int, string>,同pair<int, string> b[105],用b[i].first等来引用
vector空间的预留
vector<int> a;
误区:如果使用以上代码,在没有对a进行操作时就对a引用是错误的!栗子:
vector<int> a;
for(int i = 1; i <= n; i++)
cin >> a[i];
为什么?因为a在创建的时候并没有在内存中分配空间,所以用a[i]的形式去调用它的时候会指向未知的内存,导致程序错误(但是这在大多数编译器是不会报这个错误的,是会通过编译的,因为访问内存是合法的)
如果需要添加元素,需要使用vector的成员函数[2]push_back(v)来进行添加(下文会将),或者提前给a分配空间,代码如下
vector<int> a(100);
此行代码会创建一个vector a然后对他分配100个int的预留空间,并且全部初始化为0
易错1:分配空间是从a[0]开始分配,也就是说,a(100)只会初始化到a[99]
易错2:分配空间是用圆括号"()"而不是方括号"[]",如果用vector<int> a[100];表示创建100个vector,分别为a[0...99]
误区:在局部定义[3]vector并预留空间时,并不会像数组一样随机赋值,而是真的全部赋值为0!
作者注:如果你要提前预留空间,不如用数组,因为vector的很多函数都是依靠它的动态性,如果提前分配空间,相当于没有使用上它的动态性,不如数组
vector初始化
如果要初始化一个vector,可以用类似数组的初始化来进行
vector<int> a = {1, 2, 3, 4};
同样的,还是从 a[0]开始存!
vector添加元素
使用vector的成员函数push_back(v)添加元素
vector<int> a;
for(int i = 1; i <= n; i++)
{
int x;
cin >> x;
a.push_back(x); //0~n-1
}
易错1:如果你是这么定义vector a的,那么push_back()会从a[0]开始push,也就是说a下标的范围是0~n-1!
易错2:如果你提前为a预留了空间,比如vector<int> a(10);那么push_back()会从a[10]push进去而不是a[0]!
vector的引用
非常简单,直接a[...]就行了
vector<int> a(1);
/*input
...
*/
for(int i = 1; i <= n; i++)
cout << a[i] << " ";
vector的常用函数
- a.push_back(v) 添加元素v
- a.size() 获取a的大小,易错:如果你提前分配了100个预留空间并没有再push_back(),那么它的size就是100!
使用技巧
从a[1]开始储存
如果你想要从a[1]开始存储,可以利用上方的易错2,先为a预留一个空间来占用a[0]的位置,再push_back()
vector<int> a(1);
for(int i = 1; i <= n; i++)
{
int x;
cin >> x;
a.push_back(x); //1~n
}
for的遍历
由于你可能不知道vector目前的大小,你可以使用a.size()来获取它的大小,但是千万不要写成这个样子
for(int i = 1; i <= a.size(); i++)
因为size() 的计算是需要O(n)的时间复杂度的,这会使这一行for达到O(n²)的时间复杂度,正确代码:
int l = a.size();
for(int i = 1; i <= l; i++)
{
//do something
}
还有一种种好用的用法
vector<int> a = {1, 2, 3, 4};
for(int i : a)
cout << i;
//输出1234
在C++11中,你还可以使用autu i来遍历vector
vector<int> a = {1, 2, 3, 4};
for(auto i : a)
cout << i;
//输出1234
此时i的类型自动定义为vector a的元素的类型
vector的赋值
如果是数组的赋值,你需要一个for循环
int a[105];
//do something
int copy[105];
for(int i = 1; i <= n; i++)
copy[i] = a[i];
但是如果是vector,那么只需要一行就能搞定
vector<int> a;
//do something
vector<int> copy;
copy = a;
vector的应用
以下用vector存树并进行dfs标记
#include<bits/stdc++.h>
using namespace std;
const int N = 100;
int n, rt;
vector<int> a[N + 5]; //a[i]表示i号点所连接的点的集合
int dfn[N + 5], cnt = 0; //dfn记录每个店的dfs序,cnt是计数器
void dfs(int x, int fa) //两个参数,x表示目前的点,fa表示x的父亲节点
{
dfn[x] = ++cnt;
for(int v : a[x]) //对x所连接的点进行遍历
{
if(v == fa) continue; //如果遍历到父亲,跳过
dfs(v, x); //遍历儿子节点,此时对于节点v,它的父亲节点就是x
}
return ;
}
int main()
{
cin >> n; //输入点的个数
for(int i = 1; i < n; i++) //树只有n-1条边
{
int x, y;
cin >> x >> y; //输入连接的两个点
a[x].push_back(y); //点x与y相连
a[y].push_back(x); //点y与x相连
}
cin >> rt; //输入树的根
dfs(rt, 0);
//do something
return 0;
}
注释
[1]预留出了大量的内存:在程序执行过程中,对于每一个创建的变量,程序会对于这些变量在内存中分配空间,便于变量的读取与改变,对于单独的两个变量,预留出来的空间可能并不是连续的,而数组以及本文所讲解的vector,queue,stack等预留出来的空间则是连续的,可以参考如下图,这样对于数的调用也更快(如果读者对于指针很熟悉的话,可能会知道 &a[n]和a+n是等价的,因为a是指向a[0]的指针,a+1指的是a的下一个地址,而由于数组的内存分配是连续的,以至于a+1指向的就是a[1],也就是a[1]的地址)
[2]成员函数:类似于struct,对于以下代码,我们用a.func()来调用结构体内部函数,此时func()就是node的成员函数
struct node
{
int a, b;
void func()
{
//do something
return ;
}
};
node a;
[3]局部定义:简单来说,在int main()的外面定义的变量,我们叫它全局变量,在main()函数或其他函数内部定义的变量,我们叫他局部变量,局部变量在这一函数结束时会自动删除,这种定义叫做局部定义
#include<bits/stdc++.h>
using namespace std;
int a; //全局变量
void func()
{
int b; //局部变量
//do something
return ;
}
int main()
{
int c; //局部变量
//do something
return 0;
}