vector 是什么
学习vector,首先得需要清晰得知道其本质是什么,是一个关键字?是一种数据类型?是一个类?
vector是C++标准模板库中的部分内容,中文偶尔译作“容器”,但并不准确。它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库。vector之所以被认为是一个容器,是因为它能够像容器一样存放各种类型的对象,简单地说,vector是一个能够存放任意类型的动态数组,能够增加和压缩数据。
vector有如下特征:
1.vector是表示可变大小数组的序列容器。
2.就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
3.本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
6. 与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好。
vector 是C++ STL的一个重要成员,使用它时需要包含头文件:
#include<vector>
同时声明命名空间:
using namespace std;
vector 定义
单独定义一个vector:
vector<typename> name;
上面这个定义其实相当于是一维数组name[size],只不过其size可以根据需要进行变化,这就是“变长数组”的名字的由来。
这里的typename可以是任何基本类型,例如int、double、char、结构体等,也可以是STL标准容器,例如set、queue、vector等。
需要注意的是,如果typename也是一个STL容器,定义的时候要记得在 >> 符号之间加上空格,因为一些使用C++ 11之前标准的编译器会把它视为移位操作,导致编译错误。
如果typename 是vector,就是下面这样定义:
vector<vector<int> > name;// >>之间要加空格,前面<和里面的vector之间可加可不加空格
名为name的vector容器里,每个元素都是vector容器,这就组成了二维vector数组。
初学者可以把二维vector 数组当作两个维都可变长的二维数组理解。
另一种定义二维vector数组的方法:
vector<typename> Arrayname[size];
其中,Arrayname[0] ~ Arrayname[ arraySize - 1 ] 中的每一个元素都是一个vector容器。
与vector<vector<int> > name
不同的是,这种写法的一维长度已经固定为arraySize,另一维才是"变长"的。
vector 基本操作
(1) vector 初始化
vector<int> vec; //定义一个元素为int型的向量
vector<int> vec(5); //定义了初始大小为5个整型元素的向量(尖括号中为元素类型名,它可以是任何合法的数据类型),但没有给出初值,其值是不确定的。
vector<int> vec(10, 1); //定义初始大小为10个整型元素的向量,且给出每个元素的初值为1
vector<int> vec(tmp); //定义vec向量,用tmp向量初始化vec向量,整体复制性赋值
vector<int> tmp(vec.begin(), vec.begin() + 3); //用向量vec的第0个到第2个值初始化tmp向量
int arr[5] = {1, 2, 3, 4, 5};
vector<int> vec(arr, arr + 5); //将arr数组的元素用于初始化vec向量
//说明:当然不包括arr[4]元素,末尾指针都是指结束元素的下一个元素,
//这个主要是为了和vec.end()指针统一。
vector<int> vec(&arr[1], &arr[4]); //将arr[1]~arr[4]范围内的元素作为vec的初始值
(2) 容量
vec.size();//返回vec中元素的个数
vec.resize(10);//将a的现有元素个数调至10个,多则删,少则补,其值随机
vec.resize(10,2);//将a的现有元素个数调至10个,多则删,少则补,其值为2
vec.capacity();//返回vec在内存中总共可以容纳的元素个数
vec.empty();//当元素个数为0时返回true,否则为false
(3) 元素修改
多个元素赋值: vec.assign(); //类似于初始化时用数组进行赋值
a.assign(b.begin(), b.begin()+3); //b为向量,将b的0~2个元素构成的向量赋给a
a.assign(4,2); //是a只含4个元素,且每个元素为2
末尾添加元素: vec.push_back(a); //尾部插入数字a
末尾删除元素: vec.pop_back(); //删除向量的最后一个元素 无参数
任意位置插入元素: vec.insert();
a.insert(a.begin()+1,5); //在a的第1个元素(从第0个算起)的位置插入数值5,如a为1,2,3,4,插入元素后为1,5,2,3,4
a.insert(a.begin()+1,3,5); //在a的第1个元素(从第0个算起)的位置插入3个数,其值都为5
a.insert(a.begin()+1,b+3,b+6); //b为数组,在a的第1个元素(从第0个算起)的位置插入b的第3个元素到第5个元素(不包括b+6),如b为1,2,3,4,5,9,8,插入元素后为1,4,5,9,2,3,4,5,9,8
任意位置删除元素: vec.erase();
vec.erase(vec.begin()+2);删除第3个元素
vec.erase(vec.begin()+i,vec.end()-j);删除区间[i,j-1];区间从0开始
a.erase(a.begin()+1,a.begin()+3); //删除a中第1个(从第0个算起)到第2个元素,也就是说删除的元素从a.begin()+1算起(包括它)一直到a.begin()+3(不包括它)
vec.erase(vec.begin(),vec.end());删除所有元素,也可使用clear()
交换两个向量的元素: a.swap(b);//将a中的元素和b中的元素进行整体性交换
清空向量元素: vec.clear();
(4) 元素访问
下标访问: vec[0]; //并不会检查是否越界,下标是从0开始的。
at方法访问: vec.at(1); //以上两者的区别就是at会检查是否越界,是则抛出out of range异常
访问第一个元素: vec.front();
访问最后一个元素: vec.back();
返回一个指针: int* p = vec.data(); //可行的原因在于vector在内存中就是一个连续存储的数组,所以可以返回一个指针指向这个数组。这是C++11的特性。
使用迭代器访问元素:
vector<int>::iterator it;
for(it=vec.begin();it!=vec.end();it++)
cout<<*it<<endl;
或者:
for (size_t i = 0; i < vec.size(); i++) {
cout << vec.at(i) << endl;
}
不同访问方式的测试例程:
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char const* argv[]) {
int arr[5] = { 1, 2, 3, 4, 5 };
vector<int> vecClass(arr, arr + 5); //将arr数组的元素用于初始化vec向量
int nSize = vecClass.size();
//方法一(下标方式)打印vecClass
for (int i = 0; i < nSize; i++)
{
cout << vecClass[i] << " ";
}
cout << endl;
//方法二(at方式)打印vecClass
for (int i = 0; i < nSize; i++)
{
cout << vecClass.at(i) << " ";
}
cout << endl;
//方法三(遍历器方式)打印vecClass:输出某一指定的数值时不方便
for (vector<int>::iterator it = vecClass.begin(); it != vecClass.end(); it++)
{
cout << *it << " ";
}
cout << endl;
return 0;
}
(5) 迭代器相关
迭代器( iterator ) 可以理解为一种类似指针的东西,迭代器是一种检查容器内元素并遍历元素的数据类型。其定义是:
vector<typename>::iterator it; //可以认为迭代器就是定义一个 vector类型的指针
这样it 就是一个vector:: iterator型的变量,其中 typename 就是定义vector时填写的类型。
下面是typename为int和 double 类型的举例:
vector<int> ::iterator it;
vector<double> ::iterator it;
这样就得到了迭代器it,并且可以通过 *it 来访问vector里的元素
例如:
vector<int>::iterator it;
for(it=vec.begin();it!=vec.end();it++)
cout<<*it<<endl;
注意:由于vec的元素是int类型,因此定义迭代器时typename也要定为int,否则不可以it=vec.begin()将报错。这点也跟指针非常类似。
其中的begin()和end()是如下含义:
开始指针:vec.begin();
末尾指针:vec.end(); //指向最后一个元素的下一个位置
指向常量的开始指针: vec.cbegin(); //意思就是不能通过这个指针来修改所指的内容,但还是可以通过其他方式修改的,而且指针也是可以移动的。
指向常量的末尾指针: vec.cend();
vector 常用算法
注意:进行如下操作需要加头文件
#include<algorithm>
(1) 使用reverse将元素翻转:
reverse(array.begin(),array.end()); //将元素翻转,即逆序排列
注:在vector中,如果一个函数中需要两个迭代器,一般后一个都不包含.
(2) 使用sort排序:
默认升序:sort(array.begin(),array.end());
降序则:从大到小,可以在升序之后再使用翻转函数reverse()
也可以通过重写排序比较函数按照降序比较,如下:
定义排序比较函数:
bool Comp(const int &a,const int &b)
{
return a>b;
}
调用时:sort(vec.begin(),vec.end(),Comp)
,这样就降序排序。
(3) 复制向量的元素:
copy(a.begin(),a.end(),b.begin()+1);
//把a中的从a.begin()(包括它)到a.end()(不包括它)的元素复制到b中,从b.begin()+1的位置(包括它)开始复制,覆盖掉原有元素
(4) 查找元素的位置:
find(a.begin(),a.end(),10);
//在a中的从a.begin()(包括它)到a.end()(不包括它)的元素中查找10,若存在返回其在向量中的位置
使用 vector 的注意点
使用vector需要注意以下几点:
1、如果你要表示的向量长度较长(需要为向量内部保存很多数),容易导致内存泄漏,而且效率会很低;
为何? 前面说到,当新元素插入时候,这个动态数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。如果一个向量越长,那么当新元素插入时分配一个新的数组,然后将全部元素移到这个数组这个操作将效率越低。
2、vector作为函数的参数或者返回值时,有&代表传地址,改变其值会把原来参数的数据也改变;而如果没有&传的就是值,改变的只是传进去的局部变量,对函数外面的源数据毫无影响。
例如:
double Distance(vector<int>&a, vector<int>&b)
3、列表初始化是C++11中新定义的一种为vector对象的元素赋值,此时,用花括号括起来的0个或多个初始元素值被赋给vector对象:
vector<string> articles={“a”,“an”,“the”};
初始化的几点注意:
使用拷贝初始化时,只能提供一个初始值;
如果提供的是一个类内初始值,则只能使用拷贝初始化或使用花括号初始化;
如果提供的是初始元素值的列表,则只能把初始值都放在花括号里进行列表初始化,而不能放在圆括号中:
vector<string> articles={“a”,“an”,“the”}; //列表初始化
vector<string> articles=(“a”,“an”,“the”); //错误 不能放在圆括号中
4、下标操作不添加元素
不能使用vector 的下标操作添加元素,下标只能用于获取已存在的元素。
vector<int> ivec; // empty vector
for (vector<int>::size_type ix = 0; ix != 10; ++ix)
ivec[ix] = ix; //错误,不能使用下标操作添加元素
这个循环的正确写法应该是:
for (vector<int>::size_type ix = 0; ix != 10; ++ix)
ivec.push_back(ix); // ok: adds new element with value ix
参考鸣谢:
https://blog.csdn.net/qq_46527915/article/details/115124891
https://blog.csdn.net/wkq0825/article/details/82255984
https://blog.csdn.net/weixin_44607113/article/details/123738389
https://blog.csdn.net/ayqy42602/article/details/91043725
https://www.cnblogs.com/xiaojianliu/articles/8966073.html