STL容器之vector的介绍与使用
1.vector的介绍
- vector是表示可变大小数组的序列容器。
- 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
- 本质上来说,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要为新元素重新开辟空间。其做法是,分配一个新的数组(比原来数组大的数组),然后将全部元素拷贝(深拷贝)到这个数组。就效率而言,这是一个低效率的工作
- 实际上 , 每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小, 因为存储空间比实际需要的存储空间更大, vector的存储空间并不是一个一个增加, (不同的库采用不同的策略权衡空间, vs中是1.5倍增加,gcc是2倍), 这可能会造成空间浪费, 但不用每次增加新元素都要重新分配内存(是一种空间换时间的做法)
- 与其它动态序列容器相比(deque, list , forward_list) , vector在访问元素的时候更加高效, 在末尾添加和删除元素相对高效。对于不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好。
2.vector的使用
2.1vector的定义
//头文件
#include<vector>
vector<datatype> name;
vector<vector<datatype>> name2;//二维数组
2.2vector的常见构造
函数声明 | 接口说明 |
---|---|
vector() | 构造一个空的vector |
vector(size_type n, const value_type& val = value_type()) | 构造并初始化n个val |
vector (const vector& x) | 拷贝构造 |
vector (InputIterator first, InputIterator last) | 使用迭代器进行初始化构造 |
#include<iostream>
#include<vector>
using namespace std;
class solution {
public:
void Print_Vector(const vector<int> & v) {
for (auto i : v) {
cout << i << ' ';
}
cout << endl;
}
};
int main() {
vector<int> first;//无参构造
vector<int> second(5, 10);//构造并初始化n个val
vector<int> third(second.begin(), second.end());//迭代器初始化构造
vector<int> fourth(second);//拷贝构造
int array[] = { 2,3,4,5,6,7 };
//另外一种利用迭代器构造vector
vector<int> fifth(array, array + sizeof(array) / sizeof(int));//
solution p;
cout << "first:";
p.Print_Vector(first);
cout << "second:";
p.Print_Vector(second);
cout << "third:";
p.Print_Vector(third);
cout << "fourth:";
p.Print_Vector(fourth);
cout << "fifth:";
p.Print_Vector(fifth);
return 0;
}
2.3vector iterator的使用(vector迭代器)
迭代器名称 | 接口说明 |
---|---|
begin() | 返回第一个元素位置的iterator |
cbegin() | 返回第一个元素位置的const_iterator |
end() | 返回最后一个元素后一个位置的iterator |
cend() | 返回最后一个元素后一个位置的const_iterator |
rbegin() | 返回最后一个元素的位置的reverse_iterator |
crbegin() | 返回最后一个元素的位置的const_reverse_iterator |
rend() | 返回第一个元素前一个位置的reverse_iterator |
crend() | 返回第一个元素前一个位置的const_reverse_iterator |
- begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动。
- rbegin与rend为反向迭代器,对迭代器执行++操作,迭代器向前移动。
- cbegin与cend为const的正向迭代器,与begin和end不同的是:该迭代器指向节点中的元素值不能修改。
- crbegin与crend为const的反向迭带器,与rbegin和rend不同的是:该迭代器指向节点中的元素值不能修改。
#include<iostream>
#include<vector>
using namespace std;
//用const迭代器打印vector
void Print_Vector(const vector<int>& v) {
vector<int>::const_iterator it = v.cbegin();
while (it != v.cend()) {
cout << *it <<" ";
++it;
}
}
int main() {
vector<int> ar;
ar.push_back(6);
ar.push_back(5);
ar.push_back(4);
ar.push_back(3);
ar.push_back(2);
ar.push_back(1);
//使用迭代器正向打印出vector
vector<int>::iterator it = ar.begin();
cout << "使用正向迭代器打印出ar:" << endl;
while (it != ar.end()) {
cout << *it << " ";
++it;
}
cout << endl;
//使用迭代器进行修改vector内容
cout << "使用迭代器修改vector里的内容" << endl;
it = ar.begin();
while (it != ar.end()) {
*it *= 2;
++it;
}
//使用反向迭代器打印vector
vector<int>::reverse_iterator rit = ar.rbegin();
cout << "使用反向迭代器打印出ar:" << endl;
while (rit != ar.rend()) {
cout << *rit << " ";
++rit;//反向迭代器,对迭代器执行++操作,迭代器向前移动
}
cout << endl;
return 0;
}
2.3vector的容量操作
容量空间 | 接口说明 |
---|---|
size() | 获取元素个数 |
capacity() | 获取容量大小 |
empty() | 判断是否为空 |
clean() | 清空vector中有有效元素个数 |
resize(n) | 将有效元素的个数改成n个,多出的空间元素用用默认构造函数构造的数据填充 |
resize (n, val) | 将有效元素的个数改成n个,多出的空间元素用val填充 |
reserve (n) | 为vector预留空间(扩容, 改变的是容量的大小) (注意, 扩容只会增加, 如传入比原容量小的值则不作操作) |
1.capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。这个问题经常会考察,不要固化的认为,顺序表增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。
2.reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
3.resize在开空间的同时还会进行初始化,影响size。
capacity.cpp
#include<iostream>
#include<vector>
using namespace std;
//用const迭代器打印vector
void Print_Vector(const vector<int>& v) {
vector<int>::const_iterator it = v.cbegin();
while (it != v.cend()) {
cout << *it <<" ";
++it;
}
cout << endl;
}
int main() {
vector<int> ar;
size_t ca = ar.capacity();
for (int i = 0; i < 50; ++i) {
ar.push_back(i);
if (ca!=ar.capacity()) {
ca = ar.capacity();
cout << "ar.capacity() changed:" << ca << endl;
}
}
return 0;
}
vs下capacity是按1.5倍增长
vector的容量操作:
#include<iostream>
#include<vector>
using namespace std;
//用const迭代器打印vector
void Print_Vector(const vector<int>& v) {
vector<int>::const_iterator it = v.cbegin();
cout << "ar:";
while (it != v.cend()) {
cout << *it <<" ";
++it;
}
cout << endl;
}
int main() {
vector<int> ar;
size_t ca = ar.capacity();
for (int i = 0; i < 20; ++i) {
ar.push_back(i);
}
Print_Vector(ar);
cout <<"ar.size()=" <<ar.size() << endl;
cout <<"ar.capacity()="<<ar.capacity() << endl;
cout << endl;
//resize()
cout << "对vector进行resize()操作:" << endl;
ar.resize(24);
cout << "ar.resize(24):" << endl;
Print_Vector(ar);
cout << "ar.size()=" << ar.size() << endl;
cout << "ar.capacity()=" << ar.capacity() << endl;
cout << endl;
ar.resize(25, 1);
cout << "ar.resize(25, 1):" << endl;
Print_Vector(ar);
cout << "ar.size()=" << ar.size() << endl;
cout << "ar.capacity()=" << ar.capacity() << endl;
cout << endl;
//reserve()
cout << "对vector进行reserve()操作:" << endl;
ar.reserve(10);
Print_Vector(ar);
cout << "ar.reserve(10):" << endl;
cout << "ar.size()=" << ar.size() << endl;
cout << "ar.capacity()=" << ar.capacity() << endl;
ar.reserve(30);
Print_Vector(ar);
cout << "ar.reserve(30):" << endl;
cout << "ar.size()=" << ar.size() << endl;
cout << "ar.capacity()=" << ar.capacity() << endl;
cout << endl;
//clean()
cout << "ar.clear():";
ar.clear();
if (ar.empty()) {
cout << "ar为空!" << endl;
}
Print_Vector(ar);
cout << "ar.size()=" << ar.size() << endl;
cout << "ar.capacity()=" << ar.capacity() << endl;
return 0;
}
2.4vector增删改查
vector增删改查 | 接口说明 |
---|---|
void push_back (const value_type& val); | 尾插 |
void pop_back(); | 尾删 |
iterator insert (iterator position,const value_type& val); | 在position位置插入val |
iterator insert (iterator position, size_type n, const value_type& val); | 在position位置插入n个val |
iterator insert (iterator position, InputIterator first, InputIterator last); | 在position位置插入[first, last) 范围内所有的数据 |
iterator erase (const_iterator position); | 删除position位置处的元素 |
iterator erase (const_iterator first, const_iterator last); | 删除 [first, last) 范围内的所有元素 |
reference operator[] (size_type n); | 像数组一样访问元素 |
const_reference operator[] (size_type n) const; | const类型vector则调用此函数访问元素 |
void swap (vector& x); | 交换两个vector的数据空间 |
template <class T, class Alloc> void swap (vector<T, Alloc>& x, vector<T, Alloc>& y);(友元函数) | 交换两个vector的数据空间 |
InputIterator find (InputIterator first, InputIterator last, const T& val); | 查找val,范围是[first, last)(vector中并未提供有关查找的成员函数或者友元函数, 这个find函数是C++库所提供,) |
#include<iostream>
#include<vector>
using namespace std;
void Print_Vector(const vector<int>& v) {
vector<int>::const_iterator it = v.cbegin();
cout << "ar:";
while (it != v.cend()) {
cout << *it << " ";
++it;
}
cout << endl;
}
void Print_Vector2(const vector<int>& v) {
vector<int>::const_iterator it = v.cbegin();
cout << "ef:";
while (it != v.cend()) {
cout << *it << " ";
++it;
}
cout << endl;
}
int main() {
int num[] = { 11,12,13 };
vector<int> ae(num, num + sizeof(num) / sizeof(int));
Print_Vector(ae);
cout << endl;
//尾插 14
cout << "尾插push_back(14) push_back(15)" << endl;
ae.push_back(14);
ae.push_back(15);
Print_Vector(ae);
//尾删
cout << "尾删pop_back()" << endl;
ae.pop_back();
Print_Vector(ae);
cout << endl;
//insert操作
cout << "使用迭代器插入元素" << endl;
cout << "在头部插入元素10";
ae.insert(ae.begin(), 10);
Print_Vector(ae);
cout << "在头部插入两个元素9";
ae.insert(ae.begin(),2,9);
Print_Vector(ae);
cout << "插入一个范围内的元素";
ae.insert(ae.begin() + 2, ae.begin() + 4, ae.end());
Print_Vector(ae);
cout << endl;
//erase
cout << "使用迭代器删除元素" << endl;
cout << "删除头部元素9";
ae.erase(ae.begin());
Print_Vector(ae);
cout << "删除一个范围内的元素";
ae.erase(ae.begin() + 2, ae.begin() + 4);
Print_Vector(ae);
cout << endl;
//swap
cout << "交换两个vector中的元素" << endl;
vector<int> ef;
for (int i = 0; i < 10; ++i) {
ef.push_back(i);
}
Print_Vector2(ef);
cout << endl;
ef.swap(ae);
cout << "交换ae和ef中的元素" << endl;
Print_Vector(ae);
Print_Vector2(ef);
//查找元素find()
cout << "利用迭代器查找元素" << endl;
vector<int>::iterator i = find(ae.begin(), ae.end(), 6);
i >= ae.begin() && i <= ae.end() ? cout << "找到了!" << *i << endl : cout << "没找到!" << endl;
return 0;
}
3.vector迭代器失效问题
3.1insert引起的迭代器失效
#include<iostream>
#include<vector>
using namespace std;
int main() {
vector<int> ar;
for (int i = 0; i < 10; ++i) {
ar.push_back(i + 1);
}
vector<int> ar1(ar);
vector<int>::iterator pos = ar.begin() + 3;
cout << *pos << endl;
ar.insert(pos, 20, 0);//insert会导致迭代器失效
//是因为insert可能会导致增容增容后pos还指向原来的空间,而原来的空间已经释放了
cout << *pos<< endl;//非法访问
system("pause");
return 0;
}
以上代码编译时会出现如下错误:
当insert函数执行时, 可能会因为原来的容量不够而进行扩容, 就会动态分配新的内存空间, 将原来的数据拷贝到新空间,再释放掉旧的内存空间。这样指向旧空间元素位置的迭代器就不能继续使用了。为了规范迭代器使用, insert函数不管有没有扩容的情况, 都会强制让插入位置处的迭代器失效。
3.2erase引起的迭代器失效
#include<iostream>
#include<vector>
using namespace std;
int main() {
vector<int> qa;
for (int i = 0; i < 10; ++i) {
qa.push_back(i + 1);
}
vector<int>::iterator pos = qa.begin() + 5;
cout << *pos << endl;
qa.erase(pos);//删除pos位置的数据,导致pos迭代器失效
cout << *pos << endl;//非法访问
system("pause");
return 0;
}
以上代码编译时会出现如下错误:
erase函数是用需要删除部分元素之后将所有元素往前移动覆盖删除部分的元素,这个过程并没有insert函数那样会引起内存空间改变的情况,但还是会使迭代器失效。其原因是编写STL库的大佬们,强制要求erase函数会让被删除位置的迭代器失效。
3.3解决迭代器失效问题
erase和insert的返回值就是解决迭代器失效问题的, 我们只需要在删除或插入之后在让迭代器变量接收一下函数的返回值就可以了。
#include<iostream>
#include<vector>
using namespace std;
void Print_Vector(const vector<int>& v) {
vector<int>::const_iterator it = v.cbegin();
while (it != v.cend()) {
cout << *it << " ";
++it;
}
cout << endl;
}
int main() {
vector<int> ar;
for (int i = 0; i < 10; ++i) {
ar.push_back(i + 1);
}
vector<int> ar1(ar);
cout << "ar1:";
Print_Vector(ar1);
vector<int>::iterator pos = ar1.begin() + 5;
cout <<"pos=" <<*pos << endl;
pos = ar1.insert(pos, 5, 0);//让迭代器变量接收insert函数的返回值
cout << "ar1:";
Print_Vector(ar1);
cout << "pos=" << *pos << endl;
cout << endl;
vector<int>::iterator pos2 = ar1.begin() + 10;
cout <<"pos2="<< *pos2 << endl;
pos2 = ar1.erase(pos2);//让迭代器变量接收erase函数的返回值
cout << "ar1:";
Print_Vector(ar1);
cout << "pos2=" << *pos2 << endl;
system("pause");
return 0;
}