目录
vector的介绍
常见接口
构造函数
尾插push_back()
vector的遍历
1.用方括号+下标 遍历:
2.调用at()来访问:
3.用迭代器遍历:
4.范围for遍历:
vector的空间修改
vector增删查改
覆盖assign()
查找find()
插入insert()
删除erase()
排序sort()
string与vector的区别
本篇先对vector做一个介绍,然后把常用的接口演示一下。
由于vector的许多接口和string那儿的高度重合,所以这里就不详讲了,主要做一个了解。只要掌握几个最重要的接口,其余的用到时 查文档就好。
vector是一个类模板。它能够容纳各种类型的对象作为其元素,并且可以动态地调整大小。可以理解为动态数组。
它像数组,又不像。
像数组的是:vector也采用的连续存储空间来存储元素。这意味着可以采用下标对vector的元素进行访问,和数组一样高效。
不像数组的是:它的大小是可以动态改变的,而且它的大小会被容器自动处理。
本质来说,vector使用动态分配数组来存储它的元素。
当新元素插入时候,这个数组需要被重新分配大小,为了增加存储空间。
其做法是,分配一个新的数组,然后将全部元素移到这个数组。
就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。
不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
与其它动态序列容器相比(deque、list 、forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。
对于其他不在末尾的删除和插入操作,效率更低。
包头文件:
#include
两个标了的要记住:
构造函数声明 | 接口说明 |
---|---|
vector();(重点) | 无参构造 |
vector(size_type n, const value_type& val = value_type()); | 构造并初始化n个val |
vector (const vector& x); | 拷贝构造 |
vector (InputIterator first, InputIterator last); | 使用迭代器进行初始化构造 |
我们在使用时,要注意vector是一个类模板,要实例化成 具体的类型 来使用:
vector v1; //创建一个int类型的,空的v1
vector v2(5, 1); //v2含5个1
vector v3(v2); //拷贝构造
vector v4(v3.begin(), v3.end()); //用迭代器
vector没有头插 / 头删(因为每次都要挪数据,效率太低),也不能用operator+=,
它只能尾插 / 尾删,或者insert/erase。总之,就是一个一个地插入、删除。
#include
using namespace std;
int main()
{
vector v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
return 0;
}
相关接口:尾删pop_back()
先说明一下:
vector是不能直接用流插入输出的:
因为vector是一个容器,它可以存储多个元素。
而流插入输出操作符<<是用于将一个单独的元素插入到流中的,而不是将整个容器插入到流中。
因此,如果要将vector中的元素输出到流中,需要使用循环逐个输出。
现在我们就来看看遍历输出vector的4种方法:
vector是支持operator[]和size()的,因此可以用 方括号+下标 的方式遍历。
#include
#include
using namespace std;
int main()
{
vector v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
for (size_t i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
return 0;
}
at()是像函数一样调用的:
for (size_t i = 0; i < v.size(); i++)
{
cout << v.at(i) << " ";
}
这里补充一下at()和operator[]的区别:两者检查越界的方式不一样。
at()是较温和的抛异常:
而operator[]是断言:
vector::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
it++;
}
for (auto& e : v)
{
cout << e << " ";
}
容量空间 | 接口说明 |
---|---|
size | 获取数据个数 |
capacity | 获取容量大小 |
empty | 判断是否为空 |
resize (重点) | 改变vector的size |
reserve (重点) | 改变vector的capacity |
这些接口的功能、用法都和string里的类似,就不详讲了。
这里要说下,capacity在vs下和在g++下的增长问题:
vs下capacity是按1.5倍增长的,g++是按2倍增长的。
这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。
来测试下:
vs
#include
#include
using namespace std;
int main()
{
vector v;
int val = v.capacity();
for (size_t i = 0; i < 100; i++)
{
v.push_back(i);
if (val != v.capacity())
{
cout <<"now the capacity is: "<< v.capacity() << endl;
}
val = v.capacity();
}
return 0;
}
g++:
assign:将新内容指定给vector,替换其当前内容,并相应地修改其size。也就是说,起到一个替换、覆盖的作用。
演示:
#include
#include
using namespace std;
int main()
{
vector v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
for (auto& e : v)
{
cout << e << " ";
}
cout << endl;
v.assign(8, 8);
for (auto& e : v)
{
cout << e << " ";
}
return 0;
}
其实vector的接口里并未提供find(),不光是vector,list、deque等容器中也没有该函数。
这是因为像vector、list、deque等容器,它们都是从头到尾遍历查找,查找逻辑是相同的。
而在算法库
注意:
1.要包
2.迭代器区间,都是左闭右开的 :[ first,last )
3.返回的是迭代器。如果找到了val,返回val的迭代器;如果没找到,返回last,即右边开区间的位置。
示例:
#include
#include
#include
using namespace std;
int main()
{
vector v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector::iterator it = find(v.begin(), v.end(), 3);
cout << *it << endl;
return 0;
}
有了insert(),我们可以实现自由插入了,包括头插。
现在我们就在2的前面插入一个100:
int main()
{
vector v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
vector::iterator pos = find(v.begin(), v.end(), 2); //先找到2的位置pos
if (pos != v.end())
{
vector::iterator it = v.insert(pos, 100); //在pos位置插入100,2被挤到后一个
}
for (auto& e : v)
{
cout << e << " ";
}
return 0;
}
示例:
int main()
{
vector v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector::iterator pos = find(v.begin(), v.end(), 2);
if (pos != v.end())
{
v.erase(pos);
}
for (auto& e : v)
{
cout << e << " ";
}
return 0;
}
sort()和find()一样,都是在
➡️排序算法默认排的是升序,例:
#include
#include
#include
using namespace std;
int main()
{
vector v;
v.push_back(11);
v.push_back(8);
v.push_back(3);
v.push_back(4);
v.push_back(0);
v.push_back(4);
v.push_back(9);
v.push_back(7);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
sort(v.begin(), v.end());
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
return 0;
}
➡️那要怎么排降序呢?
用仿函数(仿函数目前不详讲,现在会用就行),库里面已经写好了,直接拿来用即可。
template
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp); //仿函数Compare comp
两个仿函数,一个叫less(<,升序),一个叫greater(>,降序),两个都是类模板。
注意:在使用时要包
示例:
#include
#include
#include
#include
using namespace std;
int main()
{
vector v;
v.push_back(11);
v.push_back(8);
v.push_back(3);
v.push_back(4);
v.push_back(0);
v.push_back(4);
v.push_back(9);
v.push_back(7);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
less lt;
greater gt;
sort(v.begin(), v.end(), lt); //升序
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
sort(v.begin(), v.end(), gt); //降序
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
return 0;
}
不过,更常见的是用匿名对象来写:
sort(v.begin(), v.end(), greater());
既然vector是类模板,那vector
答案当然是不能。
原因:
1.string结尾是有'\0'的,而vector没有。
2.很多string的接口,vector
所以,几乎不会用vector