C++STL(二)——vector容器

时间:2020-12-23 05:15:46

STL——vector容器

  • vector对象的概念
  • vector基本操作
  • vector对象的初始化、赋值
  • vector查找、替换(已在上一片 string类 博客总结过了,不再总结)
  • vector添加、弹出元素(头部、尾部)
  • vector容器的遍历(通过数组的方式、通过迭代器)
  • vector删除、插入操作(指定元素删除/插入、区间删除/插入、一次插入多个相同的元素)
  • 迭代器介绍(输入迭代器、输出迭代器、正向迭代器、双向迭代器)
  • vector举例应用

在这片文章中我参考了许多人的博客,甚至有一些部分是直接copy的,最终做了一个详细的vector用法总结

vector用法参考


vectot容器的简介

  1. vector 是一个将元素置于动态数组中加以管理的容器。
  2. vector 支持随机存取元素(支持索引值直接存取,用[]操作符或 at()方法,来下标来访问)
  3. vecotr 在尾部添加和移除元素非常快,但是在中间和头部添加和移除却很费事。
  4. vector存储数据时,会分配一个存储空间,如果继续存储,该分配的空间已满,就会分配一块更大的内存,把原来的数据复制过来,继续存储,这些性能也会一定程度上会有损耗

vector基本操作

(1). 容量
向量大小: vec.size();
向量最大容量: vec.max_size();
更改向量大小: vec.resize();
向量真实大小: vec.capacity();
向量判空: vec.empty();
减少向量大小到满足元素所占存储空间的大小: vec.shrink_to_fit(); //shrink_to_fit
(2). 修改 多个元素赋值: vec.assign(); //类似于初始化时用数组进行赋值
末尾添加元素: vec.push_back();
末尾删除元素: vec.pop_back();
任意位置插入元素: vec.insert();
任意位置删除元素: vec.erase();
交换两个向量的元素: vec.swap();
清空向量元素: vec.clear();

vector类的对象的构造

    //vector 采用模版类来实现 vector<T> vec;
vector<int> v1 //从模版实例化出存放int类型元素的 v1 容器
vector<double> v2 //从模版实例化出存放double类型元素的 v1 容器 //vector 尖括号内可以实例化出 指针类(包括自定义的数据类型的指针)型和自定义的 数据类型的 容器
vector<int *> v3// 定义存放 int型变量的地址的容器
class A {}; // 自几定义数据类型 A
vector<A> v4; //定义存放A类型数据的容器
vector<A *> v5;//定义存放 A类型变量的地址的容器 //⚠️注意:由于容器元素的初始化/赋值是按值传递复制的进行的,所以此时 A类 必须提供拷贝构造函数,以保证 A类的对象之间拷贝正常

初始化

    v1.push_back(1); //向v1中添加一个 1
v1.push_back(2); //向v2中添加一个 2
v1.push_back(3); //向v3中添加一个 3
int ar[3] = {1,3,5};
vector<int> vec={1,3,5}; //用列表初始化
vector<int>v2 = v1; //用vector的拷贝构造函数
vector<int>v3(v1);
vector<int>v4(v1.begin(),v1.end());//区间初始化
vector<int>v5(ar,ar+3); //用数组区间初始化
vector<int>v6(5,0); //用五个0进行初始化

赋值


    //vector赋值
//第一类赋值法
v4.assign(10,0); //给v4赋上10个零
v3.assign(ar,ar+2);
v2.assign(v1.begin(),v1.end());
//第二类赋值法(⚠️运用下标法 [] 必须先给vector容器申请空间,以供下标访问否则出错,如果此时用push_back()将把元素插入到先前申请的空间之后)
vector<int> v5(10); //提前申请存储10元素的空间,此时10个元素的值均为0
for(int i=0;i<10;i++)
v5[i]=i+1;
//第三类赋值法,通过迭代器的方式去赋值
for(vector<int>::iterator it =v5.begin();it<v5.end();it++)
*it = 0;

添加、弹出元素(头部、尾部)

    vector<int> v1;
//vector 的增添、弹出、删除元素
//添加
v1.push_back(1); //向v1中添加一个 1
v1.push_back(2); //向v2中添加一个 2
v1.push_back(3); //向v3中添加一个 3
print(v1);
//弹出
v1.pop_back(); //弹出尾部的一个元素
print(v1);
//尾元素的访问与修改
cout<<v1.back(); //通过back()函数去访问v1的尾元素
cout<<v1.front();//通过front()函数去访问v1的首元素
v1.back() = 10; //由于back()函数返回值为引用,所以可以通过back(),来改变尾元素的值
v1.front() = 20; // 与⬆️同理

遍历

    vector<int> v1={1,3,5};
//vector的遍历
//1.用for循环
for(int i=0;i<3;i++)
cout<<v1[i];
//2.用基于范围的for循环
for(int &i:v1)
{
cout<<i;
i = 10; //有 i 被声明的时候是以引用的方式,所以可以通过 i 来改变v1中元素的值
}
//3.用迭代器
for(vector<int>::iterator it = v1.begin();it<v1.end();it++)
{
cout<<*it;
* it = 10; //通过it指针去改变v1的值
}

删除,插入操作

    int ar[3] = { 1, 3, 5};
vector<int> v1={1,2,3};
vector<int> vec={1,3,5};
//删除操作
注意⚠️:用erase()删除完后元素后,它的返回值是一个指向下一个元素(挨着最后一个被删除的元素的元素)的迭代器
vec.erase(vec.begin(),vec.end()); //删除[vec.begin(),vec.end())区间的元素
vec.erase(vec.begin()); //删除vec.begin()这个位置的元素
//插入
vec.insert(vec.begin(), 0); //在vec.begin() 位置之前 插入元素
vec.insert(vec.end(), v1.begin(),v1.end()); //在vec.end() 之前插入在[v1.begin(),v1.end())区间内的元素
vec.insert(vec.begin(),10,0); //在vec.begin()之前插入10个0元素
vec.insert(vec.begin(),ar,ar+3);![ebd4c8678baf58da184575ee8ca5b528.png](evernotecid://2C354EAE-828A-4D61-888B-9453DC360564/appyinxiangcom/25418762/ENResource/p334)

迭代器


迭代器的介绍:

  • 迭代器类似于指针类型,它只想一个特定的位置,它也提供了对对象的间接访问
  • 指针是C语言中就有的东西,而迭代器是C++中才有的
  • 使用迭代器:和指针不一样的是,获取迭代器不是使用取地址符,有迭代器的类型同时拥有返回迭代器的成员,比如,容器都有的成员begin和end,其中begin成员负责返回指向容器第一个元素的迭代器,如:auto b = v.begin();end成员则负责返回指向容器的尾元素的下一个位置的迭代器,也就是说指向的是容器的一个本不存在的尾部。如果容器为空,则begin和end返回的是同一个迭代器,都是尾后迭代器。
  • 迭代器提供一个对容器对象或者string对象访问的方法,并且定义了容器范围

输入、输出迭代器

  • 输入迭代器:又叫“只读迭代器“,它只能从容器中读取元素,一次读出一个迭代器向前移,同一个输入迭代器不能遍历两次容器
  • 输出迭代器:又叫“只写迭代器”,它只能从容器总写入元素,一次写入一个迭代器向前移,同一个输出迭代器不能遍历两次容器

正向迭代器、双向迭代器

  • 正向迭代器:所谓正向迭代器指的是只能正向走,而不能反向走,一个在一个指着(停留在)一个元素多次,而且一次移动多个位置,可以通过 ++ += (不可以通过 – -= 运算符)
  • 双向迭代器:所谓正向迭代器指的是可以可以正反两个方向走,支持随机访问容器,一次移动多个位置,可以通过 ++ – += -= 运算符来完成

迭代器理解

  • 迭代器的类型:
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xP89GMwr-1574571844427)(evernotecid://2C354EAE-828A-4D61-888B-9453DC360564/appyinxiangcom/25418762/ENResource/p332)]
  • 1vector::iterator it;//it可以读写vector的元素
  • 2string::iterator it2;//it2可以读写string对象中的字符
  • 3vector::const_iterator it3;//it3只能读元素,不能写元素
  • 4string::const_iterator it4;//it4只能读字符,不能写字符

  • 迭代器的区别

    const_iterator和常量指针差不多,能读取但不能修改它所指的元素值,而iterator可读可写。如果容器或string对象是一个常量,只能使用const_iterator,如果不是常量,那么既能使用iterator又能使用const_iterator。

    如果容器或对象只需读操作而无需写操作的话最好使用常量类型(const_iterator),为了便于获取常量迭代器,C++11新引入了两个新函数,分别是cbegin和cend,类似于begin和end,不同之乎在于只能返回const_iterator.

    注意:但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素

  • 迭代器运算

1iter + n 迭代器加上一个数值仍得一个迭代器,所指位置向前移动n个元素

2iter - n 迭代器减去一个数值仍得一个迭代器,所指位置向后移动n个元素

3iter += n 等价于iter + n

4iter -= n 等价于iter - n

5iter1 - iter2 两个迭代器相减的结果是他们之间距离,其类型是名为difference_type的带符号整数

6 >、>=、<、<= 位置离begin近的元素较小


迭代器的应用

    //迭代器的应用
//1. 迭代器的声明
vector<int> v1{1,3,5};
vector<int>::iterator it1 = v1.begin(); //声明一个双向迭代器,(begin())并赋值为v1元素的首地址
vector<int>::const_iterator it2 = v1.cbegin(); //只能读容器的值,而不能改变容器的值,cbegin(),是一个指向容器首元素的地址常量,cend()同理如此
vector<int>::reverse_iterator it3 = v1.rbegin(); //声明一个反向迭代器,(rbegin()指向最后一个元素,rend()指向容器第一个元素位置之前)
for( ;it2 < v1.cend();it2++)
{
cout<<*it2;
//*it2 = 10; 这一条错误 因为 it2 是常量迭代器
}
for( ;it3 < v1.rend();it3++)
{
cout<<*it3<<endl; //输出结果:3 2 1
*it3 = 10; //通过指针去赋值
cout<<*it3<<endl;
}
  • 对rbegin()和rend()的理解

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h1dfhtf2-1574571844431)(evernotecid://2C354EAE-828A-4D61-888B-9453DC360564/appyinxiangcom/25418762/ENResource/p334)]

vector与迭代器 举例应用

//关于STL中vector容器的学习,编译运行后边看代码,边看执行结果效果更佳,不过看别人的代码一百遍,不如自己动手写一遍
#include <vector>//头文件
#include <iostream>
#include <algorithm>
using namespace std; void print(vector <int> v);
bool mycmpare(const int &a, const int &b){
return a>b;
}
int main ()
{
//创建vector对象三种常用的方式,此处存储元素类型是int,还可以是double、char、long long等基本数据类型,甚至是string基本字符序列容器
vector <int> v1;//不指定容器的元素个数的定义一个用来存储整型的向量容器
cout<<"v1:"<<endl;
print(v1);
/*运行结果
v1:
大小为:0
*/ vector <int> v2(5);//指定容器的元素个数的定义一个大小为10的用来存储整型的向量容器,默认初始化为0
cout<<"v2:"<<endl;
print(v2);
/*运行结果
v2:
大小为:5
0 0 0 0 0
*/ vector <int> v3(5,1);//也可指定初始值,此处指定为1
cout<<"v3:"<<endl;
print(v3);
/*运行结果
v3:
大小为:5
1 1 1 1 1
*/ //另外事先指定不指定大小都无所谓,指定了大小也可以随时使用push_back()对vector容器进行尾部扩张
v1.push_back(1);//向空的vector容器尾部扩张,追加元素为1
cout<<"v1:"<<endl;
print(v1);
v3.push_back(2);//向已有元素的vector容器尾部扩张,追加元素为2
cout<<"v3:"<<endl;
print(v3);
/*运行结果
v1:
大小为:1
1 v3:
大小为:6
1 1 1 1 1 2
*/ //插入元素使用insert()方法,要求插入的位置是迭代器的位置,而不是元素的下标
v3.insert(v3.begin(),3);//在最前面插入3
cout<<"v3:"<<endl;
print(v3); v3.insert(v3.end(),3);//在末尾追加3,此处等同于push_back()
cout<<"v3:"<<endl;
print(v3);
/*运行结果
v3:
大小为:7
3 1 1 1 1 1 2 v3:
大小为:8
3 1 1 1 1 1 2 3
*/ int i;
for(i=0;i < v3.size();i++){//只可赋值到已扩张位置
v3[i]=i;
}
//要删除一个元素或者一个区间中的所有元素时使用erase()方法
v3.erase(v3.begin()+2);//删除第2个元素,从0开始计数
cout<<"v3:"<<endl;
print(v3);
/*运行结果
v3:
大小为:7
0 1 3 4 5 6 7
*/
v3.erase(v3.begin()+1,v3.begin()+3);//删除第1个到第3个元素区间的所有元素
cout<<"v3:"<<endl;
print(v3);
/*运行结果
v3:
大小为:5
0 4 5 6 7
*/
//由结果可知,erase()方法同insert()方法一样,操作的位置都只是迭代器的位置,而不是元素的下标 //要想清空vector(),使用clear()方法一次性删除vector中的所有元素
cout<<"v2:"<<endl;
print(v2);
/*运行结果
v2:
大小为:5
0 0 0 0 0
*/
v2.clear();
if(v2.empty()) cout<<"v2经过使用clear()方法后为空\n";
print(v2);
/*运行结果
v2经过使用clear()方法后为空
大小为:0
*/ //要想将向量中某段迭代器区间元素反向排列,则使用reverse()反向排列算法,需要添加algorithm头文件
cout<<"v3反向排列前:"<<endl;
print(v3);
reverse(v3.begin(),v3.end());//全部反向排列
cout<<"v3反向排列后:"<<endl;
print(v3);
/*运行结果
v3反向排列前:
大小为:5
0 4 5 6 7 v3反向排列后:
大小为:5
7 6 5 4 0
*/ //要想将向量中某段迭代器区间元素进行排序,则使用sort()算法
cout<<"v3升序排列前:"<<endl;
print(v3);
sort(v3.begin(),v3.end());//默认升序排列
cout<<"v3升序排列后:"<<endl;
print(v3);
/*运行结果
v3升序排列前:
大小为:5
7 6 5 4 0 v3升序排列后:
大小为:5
0 4 5 6 7
*/ //自定义排序比较函数,此处降序
cout<<"v3降序排列前:"<<endl;
print(v3);
sort(v3.begin(),v3.end(),mycmpare);
cout<<"v3降序排列后:"<<endl;
print(v3);
/*运行结果
v3降序排列前:
大小为:5
0 4 5 6 7 v3降序排列后:
大小为:5
7 6 5 4 0
*/
} void print(vector <int> v)
{
//cout<<"下标方式访问:"<<endl;
cout<<"大小为:"<<v.size()<<endl;
int i;
for(i=0;i< v.size();i++){
cout<<v[i]<<' ';
}
cout<<endl<<endl; /*cout<<"用迭代器访问:"<<endl;
//定义迭代器变量it,类型与容器元素类型保持一致
vector<int>::iterator it;
for(it=v.begin(); it != v.end(); it++){
cout<<*it<<' ';
}
cout<<endl<<endl;*/
}