文章目录
vector 的相关知识
1.vector的概述
- vector是表示可变大小数组的序列容器。
- 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
- 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
- vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
- 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
- 与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在
末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好。
vector容器类型,而vector<数据类型> 定义的s是对象类型
vector< int > a;
a为定义的vector< int >类型的对象
string中的数据是以’\0’结束的,而vector中的数据不是以’\0’结束,其按指针来标记
vector中可以管理不同类型的数据
用vector实例化对象
vector<int > v; //vector<int>才是对象v的类型
用vector实例化对象并进行初始化的方式
vector<char> v2(10,'x');
vector<int> v4(10, 2);
//两种方式都是将实例化对象的前10个元素初始化为指定的值
- 或者是这种形式的使用
string s("hello bit!");
vector<char> v3(s.begin(),s.end());
利用迭代器的方式来初始化实例化的对象v3;
2.关于vector类型迭代器的使用和遍历
2.1:vector iterator 的使用
iterator的使用 | 接口说明 |
---|---|
begin() | 获取第一个数据位置的iterator |
end() | 获取最后一个数据的下一个位置的iterator |
rbegin() | 获取最后一个数据位置的reverse_iterator |
rend() | 获取第一个数据前一个位置的reverse_iterator |
cbegin() | 获取第一个数据位置的const_iterator |
cend() | 获取最后一个数据的下一个位置的const_iterator |
#include<iostream>
#include<vector>
using namespace std;
int main(){
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
//以尾插的方式将内容添加到容器中
for (int i = 0; i < v.size(); i++){
cout << v[i] << " ";
}
cout << endl;
//普通迭代器方式访问
//vector<int>::iterator vit = v.begin();
auto vit = v.begin();
//使用auto关键字后其自动匹配迭代器类型
while (vit!=v.end()){
cout << *vit << " ";
//需要输出的是迭代器中的内容
vit++;
}
cout << endl;
//反向迭代器的访问方式
auto vit1 = v.rbegin();
while (vit1 != v.rend()){
cout << *vit1 << " ";
vit1++;
}
cout << endl;
//遍历迭代器中内容的时候不能改变其内容const 迭代器的使用
vector<int>::const_iterator cvit = v.begin();
while (cvit != v.end()){
cout << "const:" << *cvit<<" ";
cvit++;
}
cout << endl;
//基于范围的for循环,内部调用迭代器的begin和end;
for (auto &e : v){
cout << e << " ";
//e++;//此处的e匹配的是迭代器中的元素
//若先++后输出的话就改变了迭代器本来要输出的内容
}
cout << endl;
return 0;
}
该程序中利用不同的方式遍历了整个容器,在定义迭代器的时候除了使用auto关键字自动匹配外,还在使用vector定义迭代器。但是定义时要注明类型:其使用方式为 vector<>的形式
#include<iostream>
#include<vector>
using namespace std;
void printVector(vector<int> &v){
//对于只是简单打印的可以传引用,进而不进行值拷贝
//若希望不能修改迭代器中的内容,传参数的时候可以传const修饰的参数
//形如:const vector<int> &v;
auto vit1 = v.begin();
while (vit1 != v.end()){
cout << *vit1 << " ";
vit1++;
}
cout << endl;
}
int main(){
vector<int> v(10, 1);
printVector(v);
//max_size();显示迭代器中可以存放的最多元素数量
vector<int> v1;
vector<char> v2;
vector<double> v3;
cout << v1.max_size() << endl; //1G
//查看指定对象的最大容量
cout << v2.max_size() << endl; //4G
cout << v3.max_size() << endl; //0.5G
return 0;
}
2.1:vector 空间增长问题
容量空间 | 接口说明 |
---|---|
size() | 获取数据个数 |
capacity() | 获取容量大小 |
empty() | 判断是否为空 |
void resize (size_type n, value_type val = value_type()); | 改变vector的size |
void reserve (size_type n); | 改变vector放入capacity |
-
capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。
-
reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问
题。 -
resize在开空间的同时还会进行初始化,影响size。
#include<iostream>
#include<vector>
using namespace std;
int main(){
//vector::capacity()的使用
vector<int> v(10,5);
cout<<v.capacity()<<endl;
//vector::reserve()的使用
v.reserve(20);
cout<<v.capacity()<<endl;
vector<int>::iterator vt=v.begin();
while(vt!=v.end()){
cout<<*vt<<" ";
vt++;
}
cout<<endl;
v.resize(30,1);
vector<int>::iterator vt1=v.begin();
while(vt1!=v.end()){
cout<<*vt1<<" ";
vt1++;
}
cout<<endl;
return 0;
}
- 运行结果
3.vector 增删改查
vector增删查改 | 接口说明 |
---|---|
void push_back (const value_type& val); | 尾插 |
void pop_back(); | 尾删 |
InputIterator find (InputIterator first, InputIterator last, const T& val); | 查找。(注意这个是算法模块实现,不是vector的成员接口) |
iterator insert (iterator position, const value_type& val); | 在position之前插入val |
iterator erase (iterator position); | 删除position位置的数据 |
void swap (vector& x); | 交换两个vector的数据空间 |
reference operator[] (size_type n); | 像数组一样访问 |
3.1.push_back() 和 pop_back() 接口的使用
- push_back和pop_back()
#include<iostream>
#include<vector>
using namespace std;
void printVector(vector<int> &v){
//打印函数
auto vit1 = v.begin();
while (vit1 != v.end()){
cout << *vit1 << " ";
vit1++;
}
cout << endl;
}
//insert接口的测试
void testVector(){
vector<int> v;
//vector容量初始值为0(不存放内容的时候)
//之后扩容的时候window按照原来容量的1.5倍进行扩容
//Linux按照原来的2倍进行扩容
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.pop_back();
v.insert(v.begin(), 0); //头插
v.insert(v.end(), 6); //尾插
//函数的参数不再是位置,而是迭代器
printVector(v);
v.insert(++v.begin(),10,20);
//迭代器可以++;首元素的下一个元素
printVector(v);
}
//扩容的是否会导致迭代器失效
//相当于一个野指针
int main(){
testVector();
return 0;
}
3.2.find/insert/erase接口的使用
- find/insert/erase
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
void printVector(vector<int> &v){
auto vit1 = v.begin();
while (vit1 != v.end()){
cout << *vit1 << " ";
vit1++;
}
cout << endl;
}
void Print(vector<int> &v){
auto vit=v.begin();
while(vit!=v.end()){
cout<<*vit<<" ";
vit++;
}
cout<<endl;
}
void Testfind() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
Print(v);
vector<int>::iterator pos=find(v.begin(),v.end(),9);
//没有找到的时候,返回的是v.end()的位置
v.insert(pos,6);
//指定位置插入内容,将后面的内容往后弄
Print(v);
}
//erase删除位置的迭代器
void testErase(){
vector<int> v(10, 2);
v.erase(v.begin());
printVector(v);
v.erase(v.begin(), v.end()-3);
//迭代器可以进行加减运算
printVector(v);
}
int main(){
testErase();
Testfind();
return 0;
}
若删除的是一个元素(参数是删除位置的迭代器)
iterator erase(iterator pos);
删除的是连续几个元素的话(参数为两个迭代器)
iterator erase(iterator first,iterator last);
从迭代器first开始删除到迭代器last;
4.迭代器失效
迭代器失效:迭代器指向的位置无效/越界
4.1.insert(iterator it,T x);
插入可能导致增容,会释放原有的空间,开辟新的空间,拷贝原有的内容
插入之前的迭代器位置可能已经被释放,变成类似为野指针的迭代器
调用完insert接口,重新获取迭代器,防止迭代器失效
4.2.调用erase(iterator it);
访问越界,调用完erase接口,获取erase接口的返回值,防止迭代器失效
#include<iostream>
#include<vector>
using namespace std;
void printVector(vector<int> &v){
//打印函数
auto vit1 = v.begin();
while (vit1 != v.end()){
cout << *vit1 << " ";
vit1++;
}
cout << endl;
}
void testErase(){
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
auto vit = v.begin();
while (vit != v.end()){
if (*vit % 2 == 0)
vit=v.erase(vit);
//调用完erase接口,获取erase接口的返回值,防止迭代器失效
//若不用vit去接收这个接口返回值的话,释放一次后程序奔溃
//这样赋值的目的是防止迭代器指向的位置是一个无效的位置。
//拿取erase接口的返回值(返回值为迭代器)
else
vit++;
}
printVector(v);
}
int main(){
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
testErase();
//auto vit = v1.begin();
//v1.insert(vit,10);
//在插入的时候进行了扩容操作,该过程中重新开辟了空间
//导致原来的空间失效(迭代器失效),
auto vit = v1.begin();
v1.insert(vit, 10);
vit = v1.begin();
//调用完insert接口,重新获取迭代器,防止迭代器失效
*vit = 20; //本质:野指针(指向已经释放的空间)
v1.push_back(3);
v1.push_back(4);
printVector(v1);
return 0;
}
5.swap函数的使用
全局函数swap和成员函数swap两个函数本质上没有区别
string类中的swap函数
全局函数:swap(s1,s2)–>{s1.swap(s2)}
本质上内部调用的为string的成员函数
成员函数:swap(s2)–>s1.swap(s2);
全局的swap只是对成员的swap函数进行了封装
通用全局变量:swap(T& s1,T& s2)–>{T temp=s1;s1=s2;s2=temp;} 内部进行了拷贝
vector的swap函数
全局函数:swap(s1,s2)–>{s1.swap(s2)}//本质上内部调用的为vector的成员函数
成员函数:swap(s2)–>s1.swap(s2);
全局的swap只是对成员的swap函数进行了封装
#include<iostream>
#include<veector>
using namespace std;
void printVector(const vector<int> &v){
for (auto& e : v){
cout << e << " ";
}
cout << endl;
}
int main(){
vector<int> v1(3, 2);
vector<int> v2(3, 4);
printVector(v1);
printVector(v2);
//全局的交换函数
swap(v1, v2);
cout << "全局函数交换后:" << endl;
printVector(v1);
printVector(v2);
//成员函数
v1.swap(v2);
cout << "成员函数交换后:" << endl;
printVector(v1);
printVector(v2);
return 0;
}
6.手动释放vector的空间
1.在一个局部域中定义一个空的vector
2.调用vector swap函数,完成空间的交换
3.出了局部域,编译器自动调用局部域中定义的局部变量的析构函数,完成空间的释放{vector < T > tmp; tmp.swap(v);}
#include<iostream>
#include<vector>
using namespace std;
int main(){
vector<int> v(90, 1);
cout << v.capacity() << endl;
//No1:
{
vector<int> v1;
//定义的时候没有初始化,其内部没有内容容量为0
v1.swap(v);
}//加了{}不会将v原来的空间释放掉
//(加了{}会在出作用域的时候调用析构函数释放局部变量v1)
cout << "No1:"<<v.capacity() << endl;
//No2:
vector<int> v1(90, 1);
cout << v1.capacity() << endl;
v1.resize(0);//改变v1的有效字符数
v1.shrink_to_fit(); //释放空间
cout << "No2:" << v1.capacity() << endl;
return 0;
}
OJ中的使用
题目要求:查找一个数组中没有重复项的那个数
class Solution {
public:
int singleNumber(vector<int>& nums) {
int value = 0;
for(size_t i =0 ; i < nums.size(); ++i){
value ^= nums[i];
}
return value;
}
};
//在c++11中
class Solution {
public:
int singleNumber(vector<int>& nums) {
int value = 0;
for(const auto& e:nums){
value ^= e;
}
return value;
}
};
//迭代器值的交换接口
v1.swap(v2);