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,就职奇安信多年,以实战工作为基础对安全知识体系进行总结与归纳,著作适用于快速入门的 《网络安全自学教程》,内容涵盖系统安全、信息收集等…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...