C++基础——vector的详解与运用
vector的介绍
文档介绍
- vector是表示可变大小数组的序列容器。
- 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
- 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
- vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
- 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
- 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。
vector的使用
下面是对于vector的基本用法的讲解
vector的初始化
| (constructor)构造函数声明 | 接口说明 |
|---|---|
| vector()(重点) | 无参构造 |
| vector(size_type n, const value_type& val = value_type()) | 构造并初始化n个val |
| vector (const vector& x); (重点) | 拷贝构造 |
| vector (InputIterator first, InputIterator last); | 使用迭代器进行初始化构造 |
主要使用的是无参构造和拷贝构造,其他两种不常用,下面代码一并介绍
void test1()
{//1.vector()(重点) | 无参构造 vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (auto c : v1){cout << c << " ";}cout << endl;//2.vector(size_type n, const value_type& val = value_type()) | 构造并初始化n个valvector<int> v2(10, 0);for (auto c : v2){cout << c << " ";}cout << endl;//3.vector(const vector & x); (重点) | 拷贝构造 vector<int> v3(v1);for (auto c : v3){cout << c << " ";}cout << endl;//4.vector(InputIterator first, InputIterator last);vector<int> v4(v2.begin(), v2.end());for (auto c : v4){cout << c << " ";}cout << endl;
}
int main()
{test1();return 0;
}
vector iterator迭代器的使用
vector的迭代器有两种,正向和反向迭代器
正向:begin() 和 end() 分别表示获取第一个数据的位置,获取最后一个数据的下一个位置
反向:rbegin() 和 rend() 分别表示获取最后一个数据的位置,获取第一个元素的前一个位置
//迭代器的名称分别为 iterator 和reverse_iterator
void test2()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);vector<int>::iterator it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;//auto rit=v.rbegin();vector<int>::reverse_iterator rit = v.rbegin();while (rit != v.rend()){cout << *rit << " ";++rit;}cout << endl;for (auto c : v){cout << c << " ";}cout << endl;
}
int main()
{test2();return 0;
}
vector 空间增长问题
使用几种函数来管理和查看vector的空间
| 容量空间 | 接口说明 |
|---|---|
| size | 获取数据个数 |
| capacity | 获取容量大小 |
| empty | 判断是否为空 |
| resize | 改变vector的size |
| reserve | 改变vector的capacity |
主要是对于resize和reserve的使用,empty就是判别是否为空,size和capacity是查看当前vector的数据个数和容量
void test3()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);cout << v.size() << endl;cout << v.capacity() << endl;v.resize(20);cout << "更改size为20" << endl;cout << v.size() << endl;cout << v.capacity() << endl;v.reserve(40);cout << "更改capacity为40" << endl;cout << v.size() << endl;cout << v.capacity() << endl;v.resize(50);cout << "更改size为50" << endl;cout << v.size() << endl;cout << v.capacity() << endl;
}
void test4()
{vector<int> v;size_t ans = v.capacity();for (int i = 0; i < 200; i++){v.push_back(i);if (ans != v.capacity()){cout << "capacity为:" << ans << endl ;}ans = v.capacity();}
}int main()
{test4();return 0;
}
注意:capacity的增长倍数是由环境决定的,vs下在1.5倍数左右,在Linux下近乎2倍,且关于resize和reserve,我们只需要知道resize底层是调用reserve的,满足size<=capacity
vector的增删改查
| vector的增删改查 | 接口说明 |
|---|---|
| push_back | 尾插 |
| pop_back | 尾删 |
| find | 查找指定的数据,返回下标(在算法模块,不在vector的成员函数中) |
| insert | 在pos位置上插入指定数值val |
| erase | 删除指定pos位置上的数据 |
| swap | 交换两个vector对象的数据空间 |
| operator[ ] | 和数组一样,通过下标方括号来访问vector的数据 |
STL分为两大模块,容器和算法,容器例如vector、list、set等,算法是在头文件<algorithm>中,是将容器中通用的函数归档在<algorithm>中,按照迭代器的不同,不同容器能使用相同或不同的函数。
push_back 和 pop_back 以及operator[ ]的使用
简单理解,不必做详解
void test5()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);//push_back的使用,传参val const value_type& val value_type实际就是T 模板类for (auto c : v){cout << c << " ";}cout << endl;//pop_back 的使用,不必传参,尾删v.pop_back(); for (auto c : v){cout << c << " ";}cout << endl;//operator[]的使用 可以访问指定下标位置元素,也可赋值cout << v[2] << endl;v[2] = 10;cout << v[2] << endl;
}
int main()
{test5();return 0;
}
find
在<algorithm>算法中,InputIterator find (InputIterator first, InputIterator last, const T& val);
#include<algorithm>
void test6()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);auto it =find(v.begin(), v.end(), 5);if (it != v.end()){cout << *it << endl;}else if(it == v.end()){cout << "没有找到val,返回的是v.end()" << endl;}
}
int main()
{test6();return 0;
}
insert
insert的插入,实际上就是先得到要插入的数值个数n,然后将vector中的元素后移n位,然后依次填入插入的数据
void test7()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto c : v){cout << c << " ";}cout << endl;//1.iterator insert (iterator position, const value_type& val); auto it=v.insert(v.begin() + 1, 10);cout << "返回迭代器指向的数值为:" << *it << endl;for (auto c : v){cout << c << " ";}cout << endl;//2.void insert (iterator position, size_type n, const value_type& val);v.insert(v.begin(), 5, 6);for (auto c : v){cout << c << " ";}cout << endl;//3.void insert(iterator position, InputIterator first, InputIterator last);v.insert(v.begin(), v.begin(), v.end());for (auto c : v){cout << c << " ";}cout << endl;vector<int> v1; //后两个参数,只要是迭代器即可,可以是其他容器的迭代器数据插入过来v1.insert(v1.begin(), v.begin(), v.begin()+6);for (auto c : v1){cout << c << " ";}cout << endl;}
int main()
{test7();return 0;
}
erase
void test8()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);//指定pos迭代器删除auto it =v.erase(v.begin()); //返回的是删除之后的v.begin()cout << *it << endl;for (auto c : v){cout << c << " ";}cout << endl; //区间删除it=v.erase(v.begin() + 1, v.begin()+2);cout << *it << endl; //返回的是删除之后的v.begin() + 1 但是要注意越界问题for (auto c : v){cout << c << " ";}cout << endl;
}
int main()
{test8();return 0;
}
迭代器失效
迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,所以迭代器失效实际上就是迭代器底层对应指针所指向的空间被销毁了,而使用一窥啊已经倍释放的空间,当继续使用已经失效的迭代器,会造成程序崩溃。
迭代器失效的情景
- 引起底层空间的改变都有可能是造成迭代器失效,如resize、reserve、insert、push_back等。他们的同意特性就是会在底层调用reserve来判断增加元素时,是否需要扩容。
- 指定位置元素的删除操作,erase(pos)
第一种场景,统一导致迭代器失效的是,由于增加元素或者手动扩容,导致vector扩容,然而扩容机制,底层是新建一个T指针数组,拷贝原有数据到新数组中,然后释放原来的数组空间,所以迭代器会失效(此时的迭代器指向的是一块被释放的空间),导致程序崩溃
void test9()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);//底层的改变实际上就是是否扩容了//先获得当前vector的首元素的迭代器vector<int>::iterator it = v.begin(); cout << *it << endl;//1.如果经历了resize ,触发扩容,迭代器失效 代码为 -1073741819。//v.resize(50, 0);//cout << *it << endl;//2.如果使用reserve手动扩容的话,也会导致迭代器失效//v.reserve(100);//cout << *it << endl; //代码为 -1073741819。//3.如果是插入元素,导致扩容,也会导致迭代器失效//v.insert(v.begin(), 100, 1);//cout << *it << endl; //代码为 -1073741819。//4.如果是push_back 导致扩容,也会发生迭代器失效for (int i = 0; i < 100; i++){v.push_back(i);}cout << *it << endl; //代码为 -1073741819。
}
int main()
{test9();return 0;
}
第二种场景,erase函数,删除指定pos位置数据,或者删除迭代器区间的数据。我们先用auto it=v.begin()+5,获得迭代器,然后我们删除一个元素,删除之后有两种可能:1.it指向的位置还有元素,那么按照道理来说 cout一下还是能正常输出数值 2.it位置已经没有元素了,此时it指向的空间已经是vector.end()之后了,再次访问it的时候,就会报错,相当于越界了。
综合上述两种可能,因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。
void test10()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);auto it = v.begin();v.erase(v.begin());//v.erase(v.begin(), v.end());cout << *it << endl; //代码为 -1073741819
}
int main()
{test10();return 0;
}
迭代器失效解决办法:在使用前,对迭代器重新赋值即可。
总结
vector的优点:
1.支持随机访问(以[]的方式)
2.cpu高速缓存效率高
vector的缺点:
1.中间和头部删除元素效率低(要移动数组元素)
2.扩容问题(扩容会导致迭代器失效)
总结:
- vector的使用实际上大部分的函数与string中的用法相似,我们只需要知道一点的是,vector是一个可变长的连续有序序列即可,不管是空间还是物理上,数据是连续的。
- vector的初始的capacity的大小和初始化的数据个数有关,然后依次为基点,在VS平台以近似1.5倍扩容,在Linux下以2倍扩容。
- 在vector中我们初步认识到了**的存在,这是算法,包含在STL中,STL分为容器和算法,**中是总结所有容器所需要的接口,通过迭代器来实现。不同的容器有不同的迭代器,能实现不同的算法接口。
- 迭代器失效问题,是string以及vector在涉及到扩容或者erase中会发生的问题,但是erase返回参数为iterator,所以只需要再次赋值即可,而扩容导致的迭代器失效,我们只能再次对迭代器重新赋值,取得新的迭代器。随用随取,在使用前,对迭代器重新赋值。
- 综上,我们可以通过vector,不必顾忌数组的长度问题,通过相应的便捷的接口,来实现更多可能性的操作。
相关文章:
C++基础——vector的详解与运用
vector的介绍 文档介绍 vector是表示可变大小数组的序列容器。就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,…...
const指针,星号判断方法
一 示例代码 1. const char *p // 指向常量的指针 2. char const *p // 指向常量的指针 3. char * const p // 指针常量二 判断方法 const在星号左边,指向常量的指针,指针p可修改。 const在星号右边,指针常量,指针p不可修改。...
移动摄像头专网需要解vlan,如何解决
🏆本文收录于「Bug调优」专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&…...
5.27周报
这两周邻近毕业故没有很多时间来学习课余内容,另外最近身体有些不舒服【偏头痛】,所以学的内容不多,包括SVM向量机和ResNet【不包括代码复现】 1.SVM支持向量机的大概内容 1、目的: 主要内容是如何找到分类的那条线【超平面】—…...
C-数据结构-树状存储的基本实现
/* 理解和记忆递归的关键在于把握递归的本质和函数调用的过程。递归函数在每次调用时会把当前状态压入调用栈,直到满足终止条件后开始回溯。理解基准条件和递归步骤:每个递归函数都需要有基准条件(如节点为空时返回),并…...
指纹识别经典图书、开源算法库、开源数据库
目录 1. 指纹识别书籍 1.1《精通Visual C指纹模式识别系统算法及实现》 1.2《Handbook of Fingerprint Recognition》 2. 指纹识别开源算法库 2.1 Hands on Fingerprint Recognition with OpenCV and Python 2.2 NIST Biometric Image Software (NBIS) 3. 指纹识别开源数…...
嵌入式之译码器
系列文章目录 译码器嵌入式之译码器 嵌入式之译码器 系列文章目录一、译码器定义二、常见类型的译码器三、工作原理 一、译码器定义 译码器(Decoder)是一种数字电路,其主要功能是从输入的编码信号中解码出特定的信息或控制信号。 译码器通常…...
分成sum接近的2个集合,返回相对小的sum
题目描述:给定一个正数数组arr,请把arr中所有的数分成两个集合,尽量让两个集合的累加和接近,返回最接近的情况下,较小集合的累加和sum。 way:选还是不选 //arr[index...]可以自由选择,返回累加和尽量接近…...
SpringBoot前置知识01-SPI接口
SpringBoot前置知识-SPI接口 介绍 Java中SPI是一种服务发现机制,或者说是一种思想,亦是一种约定。其实JDK中的JDBC就是使用了这种用思想,JDBC在JDK中只定义了接口,并没有实现类,连接什么数据库就要引入什么数据库的驱…...
数学建模--LaTeX的基本使用
目录 1.回顾 2.设置这个页眉和页脚 3.对于字体的相关设置 4.对于这个分级标题的设置 5.列表的使用 6.插入图片 1.回顾 (1)昨天我们了解到了这个latex的使用基本常识,以及这个宏包的概念,区域的划分,不同的代码代…...
授权调用: 介绍 Transformers 智能体 2.0
简要概述 我们推出了 Transformers 智能体 2.0! ⇒ 🎁 在现有智能体类型的基础上,我们新增了两种能够 根据历史观察解决复杂任务的智能体。 ⇒ 💡 我们致力于让代码 清晰、模块化,并确保最终提示和工具等通用属性透明化…...
流媒体内网穿透/组网/视频协议转换EasyNTS上云网关如何更改密码?
EasyNTS上云网关的主要作用是解决异地视频共享/组网/上云的需求,网页对域名进行添加映射时,添加成功后会生成一个外网访问地址,在浏览器中输入外网访问地址,即可查看内网应用。无需开放端口,EasyNTS上云网关平台会向Ea…...
HTML5的标签(文本链接、图片路径详解)
目录 前言 一、文本链接 超链接表述 二、图片路径详解 绝对路径 相对路径 网络路径 前言 一、文本链接 超链接表述 HTML 使用标签<a>来设置超文本链接 超链接可以是一个字,一个词,或者一组词,也可以是一幅图像,…...
React Native 之 Linking(链接)(十五)
URL Scheme是什么 URL Scheme是一种机制,主要用于在移动应用程序中打开另一个应用程序或执行特定操作。 定义与原理: URL Scheme允许应用程序通过特定的URL格式与其他应用程序进行交互。 它通过在应用程序中注册一个自定义的URL Scheme,并在…...
Java实现图书系统
首先实现一个图书管理系统,我们要知道有哪些元素? 1.用户分成为管理员和普通用户 2.书:书架 书 3.操作的是: 书架 目录 第一步:建包 第二步:搭建框架 首先:完成book中的方法 其次:完成BookList 然后:完成管理员界面和普通用户界面 最后:Main 第三步:细分方法 1.退…...
Git提交和配置命令
一、提交代码到仓库 在软件开发中,版本控制是一个至关重要的环节。而Git作为目前最流行的版本控制系统之一,为我们提供了便捷高效的代码管理和协作工具。在日常开发中,我们经常需要将本地代码提交到远程仓库,以便于团队协作和版本…...
已解决java.lang.ExceptionInInitializerError: 初始化程序中的异常错误的正确解决方法,亲测有效!!!
已解决java.lang.ExceptionInInitializerError: 初始化程序中的异常错误的正确解决方法,亲测有效!!! 目录 问题分析 报错原因 解决思路 解决方法 分析错误栈信息 检查静态初始化块和静态变量 验证资源和配置 使用日志记录…...
报表显示中,是否具备条件格式功能设计?
**报表显示中确实具备条件格式功能设计**。条件格式是一种根据特定条件对单元格或单元格区域进行格式设置的功能,它可以帮助用户更直观地理解和分析数据。 通过条件格式,用户可以设置多种条件,如单元格值的大小、是否包含特定文本等…...
完全二叉树查找
描述 有一棵树,输出某一深度的所有节点,有则输出这些节点,无则输出EMPTY。该树是完全二叉树。 输入描述 输入有多组数据,遇到0时终止输入。 每组输入一个n(1<n<1000),然后将树中的这n个节点依次输入ÿ…...
Web安全:SQL注入之时间盲注原理+步骤+实战操作
「作者简介」:2022年北京冬奥会网络安全中国代表队,CSDN Top100,就职奇安信多年,以实战工作为基础对安全知识体系进行总结与归纳,著作适用于快速入门的 《网络安全自学教程》,内容涵盖系统安全、信息收集等…...
SEO_中小企业如何低成本做好SEO?实用方案分享
为什么中小企业需要关注SEO 在当今数字化经济时代,中小企业如果想要在竞争激烈的市场中脱颖而出,关注SEO(搜索引擎优化)是必不可少的。SEO不仅能够提升网站的搜索引擎排名,还能有效带来更多的潜在客户。许多中小企业在…...
FlutterFire云函数终极部署指南:Firebase函数一键部署前必做的10个检查
FlutterFire云函数终极部署指南:Firebase函数一键部署前必做的10个检查 【免费下载链接】flutterfire 🔥 A collection of Firebase plugins for Flutter apps. 项目地址: https://gitcode.com/gh_mirrors/fl/flutterfire FlutterFire是Firebase官…...
Flow错误处理与监控:集成Sentry实现生产级错误追踪
Flow错误处理与监控:集成Sentry实现生产级错误追踪 【免费下载链接】flow Browser-based ePub reader 项目地址: https://gitcode.com/gh_mirrors/flo/flow Flow作为一款基于浏览器的ePub阅读器,为用户提供流畅的电子书阅读体验。在开发过程中&am…...
实例 9:液体压强探究
实例 9:液体压强探究 功能介绍: 模拟U形管压强计探究液体内部压强规律。学生将探头放入液体不同深度,观察U形管高度差变化;更换不同密度的液体(水、盐水、酒精),对比压强大小。应用清晰展示“液体压强随深度增加而增大”及“液体压强与液体密度有关”的规律,并可计算具…...
多目标跟踪算法实战:从DeepSORT到Chained-Tracker的避坑指南
多目标跟踪算法实战:从DeepSORT到Chained-Tracker的避坑指南 在计算机视觉领域,多目标跟踪(Multi-Object Tracking, MOT)技术正逐渐从实验室走向工业界。不同于学术论文中那些理想化的测试场景,真实项目中的光照变化、遮挡干扰和计算资源限制…...
CentOS7服务器流量飙升?别慌,用iftop+nload五分钟定位‘吃流量’的进程
CentOS7服务器流量飙升?五分钟精准定位异常进程的侦探手册 凌晨三点,手机突然响起刺耳的告警声——服务器流量激增300%。这不是演习,而是一场真实的运维战役。本文将带你化身"流量侦探",用iftop和nload这对黄金组合&…...
SAP销售订单BAPI调用避坑指南:手把手教你处理增强字段、合作伙伴与定价(附完整ABAP代码)
SAP销售订单BAPI实战:增强字段、合作伙伴与定价的深度解决方案 当你第一次调用SD_SALESDOCUMENT_CREATE创建销售订单时,可能会遇到这样的场景:订单看似创建成功,但增强字段没值、合作伙伴角色错乱、定价条件未生效。这种"表…...
第三方系统集成若依权限校验
假设系统A是由若依管理系统进行的二次开发,保留了若依的用户、权限、角色管理功能,第三方系统B想要调我们的系统A的接口,就得先集成我们的SDK,这样就可以通过我们系统A的权限、角色校验,从而完成接口调用 一 开发若依S…...
如何统计不同电话号码的个数?—位图法
在技术领域,我们常常被那些闪耀的、可见的成果所吸引。今天,这个焦点无疑是大语言模型技术。它们的流畅对话、惊人的创造力,让我们得以一窥未来的轮廓。然而,作为在企业一线构建、部署和维护复杂系统的实践者,我们深知…...
Stable Yogi Leather-Dress-Collection企业应用:服装品牌AI趋势图快速验证系统
Stable Yogi Leather-Dress-Collection企业应用:服装品牌AI趋势图快速验证系统 1. 项目概述 Stable Yogi Leather-Dress-Collection是一款专为服装品牌设计的AI趋势图快速验证工具,基于先进的Stable Diffusion技术开发。这个工具能够帮助设计师和品牌快…...
