微型企业网络设计方案,seo网站建设公司,苏州企业网站建设开发与制作,html5模板网站#x1f466;个人主页#xff1a;Weraphael ✍#x1f3fb;作者简介#xff1a;目前学习C和算法 ✈️专栏#xff1a;C航路 #x1f40b; 希望大家多多支持#xff0c;咱一起进步#xff01;#x1f601; 如果文章对你有帮助的话 欢迎 评论#x1f4ac; 点赞#x1… 个人主页Weraphael ✍作者简介目前学习C和算法 ✈️专栏C航路 希望大家多多支持咱一起进步 如果文章对你有帮助的话 欢迎 评论 点赞 收藏 加关注✨ 目录 一、什么是vector二、vector初始化2.1 默认构造函数(常见)2.2 构造函数将n个元素拷贝给本身2.3 拷贝构造函数(常见)2.4 区间拷贝2.5 数组方式 三、迭代器的使用3.1 begin end(常见)3.2 rbegin rend 四、遍历4.1 operator[]4.2 迭代器遍历4.3 范围for 五、空间增长问题5.1 size5.2 empty5.3 resize5.4 reserve5.5 swap 六、vector的插入与删除6.1 push_back - 尾插6.2 pop_back - 尾删6.3 insert - 插入6.4 erase - 删除pos位置的数据6.5 clear - 清空所有数据 七、几个常用算法7.1 sort - 排序7.2 reverse7.3 find 八、迭代器失效问题8.1 什么是迭代器失效8.2 为什么string不存在迭代器失效问题8.3 几个常见的迭代器失效样例8.4 如何解决迭代器失效问题 一、什么是vector
vector容器和数组非常相似与普通数组的区别数组是静态空间而vector可以动态扩展。动态扩展并不是在原空间之后续接新空间而是找更大的内存空间然后将原数据拷贝新空间释放原空间。使用vector容器需要包含头文件#include vectorvector可以存储多种不同的数据类型是因为它是一个模板容器 通过使用模板参数我们可以在vector中指定要存储的数据类型例如
vectorint vi // 整型容器
vectorchar vc // 字符型容器
vectordouble vd // 浮点型容器
vectorstring vs // string类型容器
vectorvectorint // 本质是二维数组
// 等等...类模板实例化与普通类的实例化不同类模板实例化需要在类模板名字后跟然后将实例化的类型放在中即可而普通类的类名就是类型。
注意需要注意数据类型的统一性。例如如果创建了一个存储整数的vector就应该只向其中存储整数类型的数据否则可能会出现类型错误或数据损坏的问题。
二、vector初始化
2.1 默认构造函数(常见)
#include iostream
#include vector
using namespace std;
int main()
{// 默认构造函数vectorint v;return 0;
}默认构造的对象size和capacity都为0。 2.2 构造函数将n个元素拷贝给本身
【函数原型】
vectorT v(n,elem);【代码示例】
#include iostream
#include vector
using namespace std;int main()
{// 构造函数将n个元素拷贝给本身// 将3个100拷贝给本身vectorint v(3, 100);for (int i 0; i v.size(); i){cout v[i] ;}cout endl;return 0;
}【输出结果】 2.3 拷贝构造函数(常见)
【函数原型】
vector (const vector x);【代码示例】
#include iostream
#include vector
using namespace std;int main()
{// 构造函数将3个100拷贝给本身vectorint v1(3, 100);cout v1;for (int i 0; i v1.size(); i){cout v1[i] ;}cout endl;// 拷贝构造函数// v2是v1的副本vectorint v2(v1);cout v2;for (int i 0; i v2.size(); i){cout v2[i] ;}cout endl;return 0;
}
【输出结果】 2.4 区间拷贝
【函数原型】
vector(v.begin()v.end());【代码示例】
#include iostream
#include vector
using namespace std;int main()
{// 构造函数将n个元素拷贝给本身// 将5个100拷贝给本身vectorint v1;// 写入1 2 3 4 5v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);// 区间拷贝// 拷贝v1对象中的2、3、4、5vectorint v2(v1.begin() 1, v1.end());cout v2;for (int i 0; i v2.size(); i){cout v2[i] ;}cout endl;return 0;
}【输出结果】 2.5 数组方式
#include iostream
#include vector
#include string
using namespace std;int main()
{vectorint v1{ 1,2,3,4,5,6 };vectorchar v2{ h,e,l,l,o };vectorstring v3{ hello, vector };return 0;
}【输出结果】 三、迭代器的使用
3.1 begin end(常见)
大家可以认为迭代器是指针。begin指向第一个数据的位置end指向最后一个数据的下一个位置 【代码示例】
#include iostream
#include vector
using namespace std;int main()
{vectorchar v;// 插入abcdefgv.push_back(a);v.push_back(b);v.push_back(c);v.push_back(d);v.push_back(e);v.push_back(f);v.push_back(g);vectorchar::iterator begin v.begin();// begin指向第一个元素对齐解引用就能得到cout 第一个元素为 *begin endl;vectorchar::iterator end v.end();// end指向最后一个元素的下一个位置cout 最后一个元素为 *(end - 1) endl;return 0;
}【输出结果】 3.2 rbegin rend rbegin和rend是反着来的。rbegin指向的是最后一个元素rend指向的是第一个元素的前一个位置 【代码示例】
#include iostream
#include vector
using namespace std;int main()
{vectorchar v;// 插入abcdefgv.push_back(a);v.push_back(b);v.push_back(c);v.push_back(d);v.push_back(e);v.push_back(f);v.push_back(g);// vectorchar::iterator 如果认为太长// 可用autoauto vci v.rbegin();// vci指向最后一个元素cout 最后一个元素 *vci endl;auto vcc v.rend();// vcc指向第一个元素的前一个位置// 对其-1。就指向第一个元素cout 第一个元素 *(vcc - 1) endl;return 0;
}【输出结果】 四、遍历
4.1 operator[] vector底层重载了下标访问操作符[]因此可以像数组一样变量
#include iostream
#include vector
using namespace std;
int main()
{vectorchar v;// 插入abcdefgv.push_back(a);v.push_back(b);v.push_back(c);v.push_back(d);v.push_back(e);v.push_back(f);v.push_back(g);// operator[]for (int i 0; i v.size(); i){cout v[i] ;}cout endl;return 0;
}【输出结果】 4.2 迭代器遍历
#include iostream
#include vector
using namespace std;
int main()
{vectorchar v;// 插入abcdefgv.push_back(a);v.push_back(b);v.push_back(c);v.push_back(d);v.push_back(e);v.push_back(f);v.push_back(g);// 迭代器vectorchar::iterator begin v.begin();while (begin ! v.end()){cout *begin ;begin;}cout endl;// 以上用for循环也是可以的for (vectorchar::iterator begin v.begin(); begin ! v.end(); begin){cout *begin ;}cout endl;return 0;
}【输出结果】 4.3 范围for 在string类时我们讲过 范围for的底层是迭代器。 #include iostream
#include vector
using namespace std;
int main()
{vectorchar v;// 插入abcdefgv.push_back(a);v.push_back(b);v.push_back(c);v.push_back(d);v.push_back(e);v.push_back(f);v.push_back(g);// 范围forfor (auto x : v){cout x ;}cout endl;return 0;
}【输出结果】 五、空间增长问题
5.1 size 功能获取数据的有效个数 #include iostream
#include vector
using namespace std;int main()
{vectorint v{ 1,2,3,4 };cout 个数为 v.size() endl;for (int i 0; i v.size(); i){cout v[i] ;}cout endl;return 0;
}【输出结果】 5.2 empty 功能判断容器是否为空。如果为空返回1否则返回0 #include iostream
#include vector
using namespace std;int main()
{// 默认构造默认有效个数size为0vectorint vi;cout vi: vi.empty() endl;vectorchar vc{ h, e, l, l,o };cout vc: vc.empty() endl;return 0;
}【输出结果】 5.3 resize
【函数原型1】
resize(int num);功能重新指定容器的长度为num 情况1若有效数据size num则会保留前num个剩下的删除
#include iostream
#include vector
using namespace std;int main()
{vectorint v{ 1,2,3,4,5,6 };cout 改变之前的长度 v.size() endl;// 重新指定容器长度// 保留前3个v.resize(3);cout 改变后的长度 v.size() endl;cout 改变后的内容为;for (int i 0; i v.size(); i){cout v[i] ;}cout endl;return 0;
}【程序结果】 【函数原型2】
resize(int num, elem);情况2若num size则会增加有效长度。如果不指定第二个参数默认增加的内容是0
#include iostream
#include vector
using namespace std;int main()
{vectorint v1{ 1,2,3,4 };v1.resize(6);// 增加了2个,不指定第二个参数默认是0for (auto x : v1){cout x ;}cout endl;vectorchar v2{ h,i };v2.resize(6, x);for (auto x : v2){cout x ;}cout endl;return 0;
}【输出结果】 5.4 reserve 功能改变vector容器的容量。一般都是扩容。 #include iostream
#include vector
using namespace std;int main()
{vectorint v;int capacity v.capacity();for (int i 0; i 100; i){// 插入100个数据v.push_back(i);// v.capacity是输出当前容量的if (capacity ! v.capacity()){capacity v.capacity();cout 容量改变 capacity \n;}}return 0;
}【输出结果】 通过以上代码我们发现vs下的容量是按1.5倍增长的g是按2倍增长的。
然后我们再把以上代码拿到Linux环境下测试 我们发现Linux下使用的vector是按照2倍方式扩容。
因此如果已经确定vector中要存储元素大概个数可以提前将空间设置足够就可以避免边插入边扩容导致效率低下的问题了。
#include iostream
#include vector
using namespace std;int main()
{vectorint v;int capacity v.capacity();// 提前开好大小为100的容量v.reserve(100);for (int i 0; i 100; i){// 插入100个数据v.push_back(i);// v.capacity是输出当前容量的if (capacity ! v.capacity()){capacity v.capacity();cout 容量改变 capacity \n;}}return 0;
}【输出结果】 或者还能这样开空间 5.5 swap
【函数原型】
swap(vec); // 将vec与本身的元素互换【代码示例】
#include iostream
#include vector
using namespace std;int main()
{vectorint v1{1,2,3,4,5,6};v1.reserve(100);cout 交换前 endl;cout v1;for (auto x : v1){cout x ;}cout endl;vectorint v2{ 7,8,9,10,11, 12 };cout v2;for (auto x : v1){cout x ;}cout endl;cout v1的容量 v1.capacity() endl;cout v2的容量 v2.capacity() endl;// 交换v1.swap(v2);cout 交换后 endl;cout v1;for (auto x : v1){cout x ;}cout endl;cout v2;for (auto x : v1){cout x ;}cout endl;cout v1的容量 v1.capacity() endl;cout v2的容量 v2.capacity() endl;return 0;
}【输出结果】 通过以上发现swap不仅可以交换容器内容同时还达到实用的收缩内存效果
六、vector的插入与删除
6.1 push_back - 尾插
#include iostream
#include vector
using namespace std;int main()
{vectorint v;// 尾插1、2、3、4v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto x : v){cout x ;}cout endl;return 0;
}【输出结果】 6.2 pop_back - 尾删
#include iostream
#include vector
using namespace std;int main()
{vectorint v;// 尾插1、2、3、4v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto x : v){cout x ;}cout endl;// 删掉4v.pop_back();for (auto x : v){cout x ;}cout endl;return 0;
}【输出结果】 6.3 insert - 插入
【函数原型1】
iterator insert (iterator position, const value_type val);注意insert是要配合迭代器使用的
#include iostream
#include vector
using namespace std;int main()
{vectorint v;// 尾插1、2、3、4v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto x : v){cout x ;}cout endl;// 头插一个6v.insert(v.begin(), 6);for (auto x : v){cout x ;}cout endl;return 0;
}【输出结果】 【函数原型2】
void insert (iterator position, size_type n, const value_type val);【代码样例】
#include iostream
#include vector
using namespace std;int main()
{vectorint v;// 尾插1、2、3、4v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto x : v){cout x ;}cout endl;// 尾插4个6v.insert(v.end(), 4, 6);for (auto x : v){cout x ;}cout endl;return 0;
}【输出结果】 6.4 erase - 删除pos位置的数据
【函数原型1】
erase(const iterator pos);注意erase也是要配合迭代器使用的
#include iostream
#include vector
using namespace std;int main()
{vectorint v;v.push_back(1);v.push_back(2);v.push_back(3);// 删除最后一个数据v.erase(v.end() - 1);for (auto x : v){cout x ;}cout endl;return 0;
}【输出结果】 【函数原型2】
erase(const iterator startconst iterator end)功能删除某个区间的数据
【代码示例】
#include iostream
#include vector
using namespace std;int main()
{vectorint v;v.push_back(1);v.push_back(2);v.push_back(3);// 清空数据v.erase(v.begin(), v.end());for (auto x : v){cout x ;}cout endl;return 0;
}【输出结果】 除了以上方式可以清空数据clear同样也行
6.5 clear - 清空所有数据
#include iostream
#include vector
using namespace std;int main()
{vectorint v;v.push_back(1);v.push_back(2);v.push_back(3);v.clear();for (auto x : v){cout x ;}cout endl;return 0;
}【输出结果】 七、几个常用算法 注意使用库里的算法需要加上头文件#include algorithm 7.1 sort - 排序
#include iostream
#include vector
using namespace std;
#includealgorithmint main()
{vectorint a{ 4,7,1,0,5,3, 2 };// 从小到大sort(a.begin(), a.end());for (auto x : a){cout x ;}cout endl;// 从大到小sort(a.rbegin(), a.rend());for (auto x : a){cout x ;}cout endl;return 0;
}【输出结果】 7.2 reverse
#include iostream
#include vector
using namespace std;
#includealgorithmint main()
{vectorint a{ 1,2,3,4,5,6 };//逆置reversereverse(a.begin(), a.end());for (auto x : a){cout x ;}cout endl;return 0;
}【输出结果】 7.3 find
注意vector是没有提供find接口的。而我们知道vector是一个类似于数组的容器因此如果想找一个数据直接遍历即可。但是算法库提供了find
#include iostream
#include vector
using namespace std;
#includealgorithmint main()
{vectorint a{ 1,2,3,4,5,6 };// 查找4vectorint::iterator pos find(a.begin(), a.end(), 4);if (pos ! a.end()){// 找到4就删掉a.erase(pos);}for (auto x : a){cout x ;}cout endl;return 0;
}【输出结果】 八、迭代器失效问题
8.1 什么是迭代器失效 迭代器失效实际就是迭代器底层对应指针所指向的空间被销毁了而使用一块已经被释放的空间造成的后果是程序崩溃。即如果继续使用已经失效的迭代器程序可能会崩溃。 8.2 为什么string不存在迭代器失效问题
string是一个特殊的容器它是由字符组成的连续序列类似于C语言的字符串。string类会动态地管理内部存储区域确保足够的容量容纳字符串。当我们向string中插入或删除字符时并不会导致整个字符串被复制到新的内存位置因此迭代器不会失效。
对比vector它是一个动态数组它使用连续的内存存储元素。当我们向vector中插入元素时如果导致当前内存不足以容纳所有元素vector会重新分配更大的内存空间并将所有元素复制到新的内存中。这样一来原来指向旧内存中的元素的迭代器就会失效因为它们指向的位置已经改变了。
8.3 几个常见的迭代器失效样例
当涉及到插入或删除操作时我们需要注意vector迭代器的失效问题
会引起其底层空间改变的操作都有可能是迭代器失效比如resize、reserve、insert、push_back等
#include iostream
#include vector
using namespace std;int main()
{vectorint v{ 1, 2, 3 };// 1. 将有效元素个数增加到100个// 多出的位置使用6填充操作期间底层会扩容v.resize(100, 6);// 2. reserve的作用就是改变扩容大小但不改变有效元素个数// 操作期间可能会引起底层容量改变v.reserve(100);// 3. insert和尾插期间// 可能会引起扩容而导致原空间被释放v.insert(v.begin(), 0);v.push_back(10);return 0;
}以上操作可能会导致vector扩容扩容就会导致旧空间被释放掉而返回的迭代器是指向被释放的空间如果再对迭代器进行使用会引起代码崩溃。
指定位置元素的删除操作
#include iostream
#include vector
using namespace std;int main()
{int a[] { 1, 2, 3, 4, 5 };vectorint v(a, a sizeof(a) / sizeof(int));// 使用find查找4所在位置auto pos find(v.begin(), v.end(), 4);// 删除pos位置的数据v.erase(pos);// 预测打印5cout *pos endl;return 0;
}【输出结果】
理论上删除了4*pos应该是5可是为什么没有打印出来呢
我们可以通过调试来观察 当我再按F10发现pos的地址变了 理论上删除4后5应该占据4的空间然而地址却变了。因此导致了迭代器失效了。
那如果是删除最后一个数据那么结果更加明显 8.4 如何解决迭代器失效问题
在使用前对迭代器重新赋值即可。
#include vector
#include iostream
using namespace std;
int main()
{vectorint v{ 1,2,3,4,5,6,7,8,9,10};// 保留1 2 3// 剩下全删除vectorint::iterator it v.begin() 3;while (it ! v.end()){// 在使用之前重新赋值it v.erase(it);}for (auto x : v){cout x ;}cout endl;return 0;
}【输出结果】