vector使用指南

news2025/1/13 6:11:52

目录

引言

空间配置器

vector 与 string的一些差异

vector容器与string容器的一些差异

接口介绍——reserve

resize接口

shrink_to_fit 接口

operator[ ] 和 at 接口

assign接口

增删查改接口

swap接口

例题讲解


引言

vector实质上就是数据结构的顺序表,也可以简单地理解为数组,它在库中的实现与之前我们所学的string有很多相似之处,重复的地方在此不多赘述。

空间配置器

 可以看到库里vector的模板参数第一项是实例化的类型,也就是顺序表实例化时里面的元素是int还是double的......vector<int> , vector<double>.......

第二个参数是空间配置器,这个概念相对复杂一点,是一种底层实现机制,在使用层面我们可以暂时不用管。说明一下以防产生误解。

vector<char> 与 string的一些差异

首先说明vector<char>与string肯定是不一样的,就相当于char arr[3] = {'a','b','c'} 和char* arr = "abc" 一样,前者不用 '\0'结尾,是字符数组,但是后者是字符串,需要'\0'结尾。因此两者之间是有差异的,不能代替。

第二个差异在于大小的比较,vector<char>比较大小就是单纯按照数组字符个数,而string是字符串,按照ASCLL码进行比较,不按长度比较。

第三个差异在于接口上,它们之间的接口也有很大区别,功能的不同,实现上的不同等等。比如对于操作符+=的重载,string有+=,可以直接在字符串后面加字符\字符串,而vector<char>则没有这个接口。

其它的不同更多体现在vector 与 string整体的不同上,后面讲接口的时候会顺带提出。

vector容器与string容器的一些差异

上面是vector<char>与string的差异,容器本身还有很多异同之处。

string是早期设计的,与后面容器的实现有时代的隔阂,string的接口庞杂繁复,数量是vector的一倍,其实有很多接口是没有什么意义的或者基本不会使用的,但由于向前兼容的特性这些接口都保留了下来。

在insert、erase这些增删接口上面,string与其他容器(如:vector) 的标识也不同,string是使用下标标识位置的,而vector等容器是用迭代器标识位置的 (insert(v.begin()+4,1) )

接口介绍——reserve

 reserve接口主要是扩容,通过改变capacity的大小达成目的。

接口本身很简单易懂,但既然能改变capacity能扩容,那能不能缩容呢?

————一般来说是不会实现缩容的,因为缩容是有代价的,不是想缩就缩的。

缩容实质上是开一块新空间拷贝数据,是以空间换时间的方式。

缩容需要先开新空间,拷贝数据过去,然后释放旧空间,频繁缩容就会反复开空间、释放和拷贝,会有较大代价所以一般不会缩容。

有人会问:为什么不能直接缩容释放不需要的一块空间给操作系统,要开新空间拷贝数据呢?

 ————这样当然是不行的,和内存管理机制有关。

类似malloc一块空间是不能分两次free还给操作系统的。

resize接口

 resize接口是个非常重要的接口,主要是改变size,它不改变capacity,有四种情况

1、没有开空间时,resize开空间+初始化(<int>一般里面元素初始化为0)常用

2、容量不够时,扩容。如capacity为9,resize(20),此时空间不够会先扩容。

3、容量够时,会从初始size位置填数据直到当前size位置。

4、resize的size值比初始值小时会删除数据,注意不会删除空间!只是删除有效数据!

那么库里面给的接口,参数方面我们似乎看不懂,size_type 和 value_type是什么类型,这是由于库在里面再次typedef了这些类型,在Member types可以查到:

 我们可以看到size_type 其实就是size_t类型

 value_type则是第一个模板参数,也就是我们这里的T,class<T>就是实例化对象类型。

所以库里面的函数定义翻译过来就是void resize(size_t n , T =T() )

n 是改变的size的大小,T = T() 是调用了系统默认构造函数。

由此可以应证C++内置类型也需要(也有)默认构造函数的概念,因为像这种类似的场景用得到。

如果T是内置类型不是自定义类型,如何调用默认构造,所以内置类型也有默认构造函数的概念。

shrink_to_fit 接口

上面我们说了一般来说不会轻易缩容,因为代价比较大,像reserve只扩不缩,resize、clear都不动capacity,但是shrink_to_fit 接口是实现来缩容的,与上面reserve、resize相反,是 “以空间换时间 ”。

扩容一般是实现两倍扩容,像Linux等平台下都是两倍,也有些平台是1.5倍扩容,像VS,具体看编译器实现,两倍扩容一般用的比较多一点。

operator[ ] 和 at 接口

两个接口都是用来遍历访问vector中的数据的,存在着一定差异。

 

 相同之处是使用方式类似,都是通过下标获得数据。并且都提供了const和非const两个版本。

为什么要提供两个版本?————

1、只读接口函数只有const版本的(如:size函数)只能由const对象调用

2、只写函数接口只有非const版本的(如:push_back函数),只能由非const(普通)对象调用

3、可读可写接口函数,const+非const都提供(如:operator[]),两者都能调用

注意这里的[ ]是函数调用,不是单纯的操作符,与数组不同。

operator[ ]和at的区别在于越界的情况下,[ ] 内部实现的时候是assert断言下标位置pos < size的,

而at 则是通过抛异常的方式报错。(注:assert在debug下有效,relase下不产生效果)

assign接口

 assign 有2种用法:

用法 1、覆盖赋值,这种方式会覆盖掉之前所有数据。

	vector<int>a;
	a.resize(5);
	a.push_back(1);
	a.push_back(2);
	a.push_back(3);
	a.push_back(4);
	for (auto e : a)
	{
		cout << e << "";
	}
	cout << endl;

	a.assign(9, 1);

	for (auto e : a)
	{
		cout << e << "";
	}
	cout << endl;

 从结果来看确实覆盖了之前的数据。

用法 2、支持迭代器区间

库里面给的接口参数就是迭代器区间  [first,last) \ [begin,end) ,是左闭右开的。

使用方式:

	vector<int> aa;
	aa.resize(5);
	for (auto e : aa)
	{
		cout << e << " ";
	}
	cout << endl;

	size_t i = 0;
	vector<int>bb;
	bb.resize(5);
	for (auto &e : bb)
	{
		e = i++;
	}
	aa.assign(bb.begin(), bb.end());
	for (auto e : aa)
	{
		cout << e << " ";
	}
	cout << endl;

 

再看参数类型,我们发现库里给参数迭代器也写了一个模板,有人会问:直接写成iterator不就行了,为什么还要写个迭代器模板?

————因为如果单写迭代器就只支持vector下的迭代器区间,写成模板的话可以支持其他容器的迭代器区间,比如:

 这样就支持了string容器的迭代器区间。

也可以这样使用:

 与上面对比一下,发现区间确实像期望所改变了。

增删查改接口

数据结构最重要的一环可以说是增删查改了。

增:最常用的是push_back,但是push_back适用于尾插,而在顺序表中一般不建议在中间位置插入(尤其是头插),因为这样会挪动数据,效率低下。所以insert接口使用没有push_back频繁。

 另外insert可以插入一个元素,也可以插入一段数据。

删:最常用的是pop_back,但是pop_back适用于尾删,和增一样,其他情况会挪动数据,因此erase接口也使用不那么频繁。

另外删可以删除单个位置数据,也可以直接删除一段数据。

查:在vector容器库中发现没有期望的find接口,这是怎么回事?————因为这个接口在每个容器中实现的方式都是一样的,都是遍历迭代器,找到了就返回对应的迭代器位置,所以不会特地在每个容器中都写一个find,而是将它拉出来写个模板,使得每个容器都能直接使用。

 可以看到find是从first位置开始查找,到 last位置结束,找到了返回此时的迭代器,没找到就返回last位置的迭代器(这也体现了迭代器区间左闭右开,last不在区间中)。

使用方式:

	vector<int>::iterator it = find(aa.begin(), aa.end(), 101);
	if (it != aa.end())
	{
		aa.insert(it, 0);
	}
	for (auto e : aa)
	{
		cout << e << " ";
	}
	cout << endl;

 

 这里找到了就在当前迭代器位置之前插入0,没找到就不插入。

改:改的话主要是通过operator[ ] 来实现。

swap接口

 vector中自己实现了一个swap函数,因为库里的swap函数交换代价太大,需要深拷贝。

容器中实现的可以直接完成交换,大大降低了代价开销。

	vector<int>bb;
	bb.push_back(1);
	aa.swap(bb);
	for (auto e : aa)
	{
		cout << e << " ";
	}
	cout << endl;

 

例题讲解

LeetCode118.杨辉三角

 首先看到题目的图要明确一点:要实现的二维数组是每一行的元素个数不同,但并非按图上画的那般顺序排列,二维数组的图应该是这样的:

 

 题中给的接口样式需要返回的是二维数组地址,传进来的是二维数组的行数。

对于这种问题,我们可以先创建一个二维数组模型,先建立行,再通过行来创建列,从而通过二维数组的下标访问每个元素。

创建二维数组模型:vector<vector<int>> vv;

建立行:vv.resize(numRows);  //传参行数为numRows

通过行建立列(通过for循环实现):

        for (int i = 0; i < vv.size(); ++i)
        {
            vv[i].resize(i + 1, 0);
        }

 同时观察杨辉三角图,每行首末元素都为1,所以在循环中将首末元素赋值为1:

        for (int i = 0; i < vv.size(); ++i)
        {
            vv[i].resize(i + 1, 0);
            vv[i][0] = vv[i][vv[i].size() - 1] = 1;
        }

此时二维数组建立完成,首末元素为1,其余元素为0. 再观察图,发现每行非1元素 = 上一行同列元素 + 上一行同列元素前一个;

 于是用for循环将每个非1元素的值填上:

        for (int i = 0; i < vv.size(); ++i)
        {
            for (int j = 0; j < vv[i].size(); ++j)
            {
                if (vv[i][j] == 0)
                {
                    vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
                }
            }
        }

最后返回二维数组首元素地址vv就可以了。

完整代码如下:

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> vv;
        vv.resize(numRows);
        for (int i = 0; i < vv.size(); ++i)
        {
            vv[i].resize(i + 1, 0);
            vv[i][0] = vv[i][vv[i].size() - 1] = 1;
        }
        for (int i = 0; i < vv.size(); ++i)
        {
            for (int j = 0; j < vv[i].size(); ++j)
            {
                if (vv[i][j] == 0)
                {
                    vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
                }
            }
        }
        return vv;
    }
};

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/145722.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构:栈和队列(详细讲解)

&#x1f387;&#x1f387;&#x1f387;作者&#xff1a; 小鱼不会骑车 &#x1f386;&#x1f386;&#x1f386;专栏&#xff1a; 《数据结构》 &#x1f393;&#x1f393;&#x1f393;个人简介&#xff1a; 一名专科大一在读的小比特&#xff0c;努力学习编程是我唯一…

(8)Qt中的自定义信号

目录 自定义信号需要遵循的规则 信号的发送 自定义信号的基本实现 使用一个父子窗口切换的小案例 Qt框架提供的信号在某些特定场景下是无法满足我们的项目需求的&#xff0c;因此我们还设计自己需要的的信号&#xff0c;同样还是使用connect()对自定义的信号槽进行连接。 自…

制造业ERP管理系统解决方案之销售管理

在企业的生存发展中&#xff0c;销售管理起到了重要的作用&#xff0c;它决定着企业发展的提速和效益的提升。做好销售管理工作&#xff0c;不仅可以推动企业资金有效运转&#xff0c;还可以使企业在稳定中高效发展&#xff0c;使企业价值最大化。而在制造企业销售管理中&#…

Leetcode.1658 将 x 减到 0 的最小操作数

题目链接 Leetcode.1658 将 x 减到 0 的最小操作数 题目描述 给你一个整数数组 nums 和一个整数 x 。每一次操作时&#xff0c;你应当移除数组 nums 最左边或最右边的元素&#xff0c;然后从 x 中减去该元素的值。请注意&#xff0c;需要 修改 数组以供接下来的操作使用。 如…

SHELL脚本学习 --- 第八次作业(安全脚本)

SHELL脚本学习 — 第八次作业 题目要求&#xff1a; 将密码输入错误超过4次的IP地址通过firewalld防火墙阻止访问 思路&#xff1a; 首先需要找到ssh密码输入错误超过四次的IP地址&#xff0c;需要到日志文件中找。 该日志文件为/var/log/secure。 找到之后通过正则匹配到密码输…

JavaEE高阶---SpringBoot 统⼀功能处理

一&#xff1a;什么是SpringBoot 统⼀功能处理 SpringBoot统一功能处理是AOP的实战环节。我们主要学习三方面内容&#xff1a; 统一用户登录权限验证&#xff1b;统一数据格式返回&#xff1b;统一异常处理。 二&#xff1a;统一用户登录权限验证 Spring 中提供了具体的实现…

通过后端代理实现Web搜索功能

大家好&#xff0c;才是真的好。 前面我们都在说使用Domino自带的视图搜索功能&#xff0c;这一篇也是&#xff0c;不过不是视图搜索&#xff0c;而是整个Notes数据库搜索&#xff0c;然后再将结果返回给浏览器网页呈现。 前提和前面两篇都是一样的&#xff0c;即Notes应用需…

Java 如何不使用 volatile 和锁实现共享变量的同步操作

前言 熟悉 Java 并发编程的都知道&#xff0c;JMM(Java 内存模型) 中的 happen-before(简称 hb)规则&#xff0c;该规则定义了 Java 多线程操作的有序性和可见性&#xff0c;防止了编译器重排序对程序结果的影响。 按照官方的说法&#xff1a; 当一个变量被多个线程读取并且至…

「数据密集型系统搭建」原理篇|数据类型不怕精挑细选

本篇围绕MySQL数据库的底层存储模型、列类型来聊聊数据库表设计及建模中要注意的事项&#xff0c;剖析最根源的底层物理存储文件&#xff0c;用最真实的数据剖析来证明和解答开发过程中的疑惑。 在一些技术谈资、面试沟通过程中&#xff0c;MySQL特别是我们常用的Innodb存储引擎…

JavaScript 作用域

文章目录JavaScript 作用域JavaScript 作用域JavaScript 局部作用域JavaScript 全局变量JavaScript 变量生命周期函数参数HTML 中的全局变量你知道吗?JavaScript 作用域 作用域可访问变量的集合。 JavaScript 作用域 在 JavaScript 中, 对象和函数同样也是变量。 在 JavaScr…

ONES X 海银财富|以敏捷流程管理,创新金融服务平台

近日&#xff0c;ONES 签约财富管理行业领跑者——海银财富&#xff0c;助力海银财富落地敏捷流程管理&#xff0c;打造从需求到交付的一体化平台&#xff0c;快速接受业务方的反馈&#xff0c;进行金融平台的迭代与优化。海银财富管理有限公司&#xff08;以下简称海银财富&am…

拆机详解:1968年军用集成电路计算机 高级货赢在做工

halo大家好&#xff0c;这里是一天更两篇的Eric。 今天我在网上偶然看到一个拆军用计算机的&#xff0c;正好给你们分享一下。这可是1970年左右为了F4战斗机敌我识别系统打造的&#xff0c;虽说比之前说的Macintosh更加的挤也更大&#xff0c;不过做工够扎实。 上图&#xff…

centos8安装RabbitMQ和erlang

RabbitMQ 消息队列MQ RabbitMQ简称MQ是一套实现了高级消息队列协议的开源消息代理软件&#xff0c;简单来说就是一个消息中间件。是一种程序对程序的通信方法&#xff0c;其服务器也是以高性能、健壮以及可伸缩性出名的Erlang语言编写而成为什么使用MQ 在项目中&#xff0c;…

QSyntaxHighlighter

一、描述 此类用于自定义语法高亮显示规则&#xff0c;是用于实现 QTextDocument 文本高亮显示的基类。 要自定义语法高亮显示&#xff0c;必须子类化 QSyntaxHighlighter 并重新实现 highlightBlock()。此函数将在合适的时机自动被调用。 highlightBlock() 函数将格式设置应…

SOFA Weekly|SOFANews、本周贡献 issue 精选

SOFA WEEKLY | 每周精选 筛选每周精华问答&#xff0c;同步开源进展欢迎留言互动&#xff5e;SOFAStack&#xff08;Scalable Open Financial Architecture Stack&#xff09;是蚂蚁集团自主研发的金融级云原生架构&#xff0c;包含了构建金融级云原生架构所需的各个组件&#…

Rasa 3.x 学习系列-Rasa [3.4.0] - 2022-12-14新版本发布

Rasa 3.x 学习系列-Rasa [3.4.0] - 2022-12-14新版本发布 任何人都可以学习Rasa之优秀Rasa学习资源推荐 欢迎同学们报名Gavin老师的Rasa系列课程,任何人都可以学习Rasa之优秀Rasa学习资源推荐: 1.NLP on Transformers高手之路137课 2 .Rasa 3.X 智能对话机器人案例开发硬核…

五、k8s pod详解

文章目录1 pod介绍1.1 pod 定义2 pod配置2.1 基本配置2.2 镜像拉取2.3 启动命令2.4 环境变量2.5 端口设置2.6 资源配额3 Pod生命周期3.1 创建和终止3.2 初始化容器3.3 钩子函数3.4 容器探测3.5 重启策略4 Pod调度4.1 定向调度4.2 亲和性调度5 污点和容忍5.1 污点&#xff08;Ta…

做短视频必须了解的6个问题,你知道几个答案呢?

做短视频必须了解的6个问题&#xff0c;你知道几个答案呢&#xff1f; 最近好多朋友问了视频运营和创作的问题&#xff0c;把其中六个有代表性的问题和答案汇总在一起&#xff0c;公开给大家&#xff0c;希望对大家有所帮助。 1、账号被限流了怎么办&#xff1f; 随手拍十条…

聚观早报 | 小米同时研发两款车;谷歌计划向印度最高法院上诉

小米同时研发两款车&#xff1a;谷歌计划向印度最高法院上诉&#xff1b;苹果AR/MR头显部件延迟发货&#xff1b;2022年特斯拉在德汽车销量激增&#xff1b;纽约市教育部门禁止访问 ChatGPT 小米同时研发两款车小米第一款车为中型溜背式轿车&#xff08;内部代号 Modena 摩德纳…

欧科云链任煜男:推动区块链创新,切忌陷入“过度金融化”的桎梏

FTX破产轰动全球&#xff0c;揭示的是监管出现问题。FTX作为行业龙头&#xff0c;持有多国牌照却依然“暴雷”&#xff0c;未来到底应如何监管加密资产行业&#xff1f;针对这一问题&#xff0c;欧科云链控股(01499.HK)公司执行董事、董事局主席兼行政总裁任煜男近日在接受香港…