C++vector浅显了解及运用(信息学竞赛专用)涵盖各种易错、误区和技巧

当然也可以不看=>阅读我的文章前请务必先看此文章!

目录

阅读文章须知

引言

vector与数组

vector的创建

vector空间的预留

vector初始化

vector添加元素

vector的引用 

vector的常用函数

使用技巧

从a[1]开始储存

 for的遍历

vector的赋值

vector的应用

注释


阅读文章须知

为了得到良好的阅读体验,请具备稍微完备的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;
}

  • 27
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值