【C++从0到王者】第十二站:vector基本使用

news2024/11/28 20:50:29

文章目录

  • 一、vector基本介绍
  • 二、vector的基本使用
  • 三、vector\<char> 和string的区别
  • 四、vector接口介绍
    • 1.vector的模板参数
    • 2.构造函数
    • 3.迭代器
    • 4.size和max_size
    • 5.resize和reserve
    • 6.operator[]和at
    • 7.front和back
    • 8.data
    • 9.push_back和pop_back
    • 10.insert和erase
    • 11.assign
    • 12.swap和clear
  • 五、几道经典例题
    • 1.只出现一次的数字
    • 2.杨辉三角
    • 3.电话号码的字母组合

一、vector基本介绍

如下图所示,从这里我们可以得知,vector其实本质是一个动态增长的顺序表,是一个可以改变容量大小的数组。也就是说它与string的本质是一样的,而vector这个英文翻译成中文是向量的意思
在这里插入图片描述就像数组一样,vector的元素使用连续的存储位置,这意味着也可以使用指向其元素的常规指针上的偏移量来访问它们的元素,并且与数组一样高效。但与数组不同的是,它们的大小可以动态变化,容器会自动处理它们的存储。
在内部,vector使用动态分配的数组来存储它们的元素。这个数组可能需要重新分配,以便在插入新元素时增加大小,这意味着分配一个新数组并将所有元素移动到其中。就处理时间而言,这是一个相对昂贵的任务,因此,vector不会在每次向容器中添加元素时重新分配。
相反,vector容器可以分配一些额外的存储空间,以适应可能的增长,因此容器的实际容量可能大于包含其元素严格所需的存储空间(即其大小)。库可以实现不同的增长策略,以平衡内存使用和重新分配,但在任何情况下,重新分配应该只发生在对数增长的大小间隔内,以便在向量末尾插入单个元素时可以提供平摊的常数时间复杂度(参见push_back)。
因此,与数组相比,向量消耗更多的内存,以换取以有效的方式管理存储和动态增长的能力。
与其他动态序列容器(deque、lists和forward_lists)相比,vector可以非常高效地访问其元素(就像数组一样),并且可以相对高效地从其末端添加或删除元素。对于涉及在末尾以外的位置插入或删除元素的操作,它们的性能比其他操作差,并且迭代器和引用的一致性不如列表和forward_lists。

二、vector的基本使用

我们首先来大致浏览以下vector的接口,其实我们也能够大概理解这些接口的意思
在这里插入图片描述

我们可以大概的使用一些这些接口,如下所示

void testvector1()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);


	for (size_t i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;

	vector<int>::iterator vit = v.begin();
	while (vit != v.end())
	{
		cout << *vit << " ";
		vit++;
	}
	cout << endl;
}

在这里插入图片描述

处理加入内置类型之外,我们也可以加入string,vector等类类型

void testvector2()
{
	vector<string> v;
	string name("zhangsan");

	v.push_back(name);
	v.push_back(string("lisi"));
	v.push_back("wangwu");


	vector<vector<int>> vv;
}

在上述代码中,我们相对vector<string>插入一个string的方式可以有很多种,比如先构造一个string,然后尾插他,比如直接插入匿名对象,比如直接插入字符串,让这个字符串进行隐式类型转换构造欻string以后然后在插入进去

我们甚至还可以定义vector<vetor<int>>这种套娃的形式,其实这个形式本质就是一个二维数组

三、vector<char> 和string的区别

显而易见是肯定不可以相互替代

  1. 首先string和vector<char>在结构上就有所区别,string它会主动加上\0,而vector是不会的,这样一来,vector其实是不是那么跟c语言进行兼容的
  2. 其次string的接口是更加丰富的。string还有+=,比较大小的接口这些都是有实际意义的。而对于vector对于比较大小是没有意义的,虽然库里面也支持了比较大小。
  3. string还有插入一个字符,插入字符串等更加丰富的接口,而vector要针对于各种类型,所以它就没有这么多接口

总而言之,即以下两点

  1. string要求最后又\0,可以更好的兼容C语言
  2. string有很多他的专用接口函数

所以说,vector和string有各自存在的价值的

四、vector接口介绍

1.vector的模板参数

如下图所示,vector的模板参数有以下两个,一个是T,决定vector里面的数据是什么类型的,一个是缺省的模板参数,这个是一个内存池,库里面自动提供了,当然我们一般用这个也就够了,不会自己去写的

在这里插入图片描述

2.构造函数

如下所示,有如下几种构造函数
在这里插入图片描述allocator是一个内存池,他也是一个缺省参数,我们暂时不管他

不难看出,第一个是一个无参的构造函数,第二个是n个val的构造,第三个是一个迭代器区间进行初始化,第四个是拷贝构造函数

下面是前两个的使用

void testvector3()
{
	vector<int> v1;
	vector<int> v2(10, 1);
	vector<string> v3(10, "****");
	for (auto e : v2)
	{
		cout << e << " ";
	}
	cout << endl;
	for (auto e : v3)
	{
		cout << e << " ";
	}
	cout << endl;
}

在这里插入图片描述

对于第三个构造函数,我们注意到他其实是一个模板,也就是说,他可以使用任意一个迭代器进行初始化。只要类型匹配即可。

void testvector3()
{
	vector<int> v1;
	vector<int> v2(10, 1);
	vector<string> v3(10, "****");
	for (auto e : v2)
	{
		cout << e << " ";
	}
	cout << endl;
	for (auto e : v3)
	{
		cout << e << " ";
	}
	cout << endl;


	vector<int> v4(v2.begin(), v2.end());
	for (auto e : v4)
	{
		cout << e << " ";
	}
	cout << endl;

	vector<string> v5(v3.begin(), v3.end());
	for (auto e : v5)
	{
		cout << e << " ";
	}
	cout << endl;

	string s("hello world");
	vector<char> v6(s.begin(), s.end());
	for (auto e : v6)
	{
		cout << e << " ";
	}
	cout << endl;

	//数组名其实就是一个天然的迭代器
	int a[] = { 1,2,3,4 };
	vector<int> v7(a, a + 4);
	for (auto e : v7)
	{
		cout << e << " ";
	}
	cout << endl;
}

在这里插入图片描述

除此之外,我们知道。stl有容器和算法,而算法中比如说sort他就是支持各种类型的容器进行排序,因为其是用模板的,对某一个迭代器区间进行排序
在这里插入图片描述

我们可以去使用一下,如下所示,库里面默认是排升序,即<,我们如果要排升序的话,可以直接使用greater<int>对象。这个类型库里面已经定义好了,我们直接用就可以,也可以直接用他的匿名对象

void testvector4()
{
	vector<int> v;
	v.push_back(151);
	v.push_back(1);
	v.push_back(1511);
	v.push_back(12);
	v.push_back(11);
	v.push_back(10);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	sort(v.begin(), v.end());
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	//greater<int> gt;
	//sort(v.begin(), v.end(), gt);
	sort(v.begin(), v.end(), greater<int>());
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

}

在这里插入图片描述

还有如下的使用等

void testvector5()
{
	string s("hello world");
	sort(s.begin(), s.end());
	cout << s << endl;

	int a[] = { 456,12,11,151 };
	sort(a, a + 4);
	for (auto e : a)
	{
		cout << e << " ";
	}
	cout << endl;
}

在这里插入图片描述

3.迭代器

他有如下的几种迭代器

下面是正向迭代器,前者支持读写,后者只读
在这里插入图片描述

下面是反向迭代器,前置支持读写,后者只读
在这里插入图片描述

正向迭代器的使用很简单,我们现在来看一下反向迭代器的使用

void testvector6()
{
	vector<int> v;
	v.push_back(151);
	v.push_back(1);
	v.push_back(1511);
	v.push_back(12);
	v.push_back(11);
	v.push_back(10);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	sort(v.rbegin(), v.rend());
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	//greater<int> gt;
	//sort(v.rbegin(), v.rend(), gt);
	sort(v.rbegin(), v.rend(), greater<int>());
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

在这里插入图片描述

不难得出,反向迭代器可以直接排降序。

4.size和max_size

如下是size这个接口,他的作用是返回当前元素的个数。这个还是比较常用的一个接口

在这里插入图片描述

如下是max_size,这个接口在实际中并无太大的用处,他的功能是返回vector所能容纳的最大元素数。
在这里插入图片描述

5.resize和reserve

这两个接口与string是类似的,resize是开空间+填数据,而reserve仅仅只是开空间,不改变size的值。

下面是我们容易犯的一个错误

void testvector7()
{
	vector<int> v;
	v.reserve(10);
	for (int i = 0; i < 10; i++)
	{
		v[i] = i;
	}
}

在这里插入图片描述

这里出现错误的原因其实就是因为我们混淆了reserve和resize,reserve他改变的仅仅只是capacity,不会去改变size,这样一来的话,我们就会产生越界现象。自然就会报错了。

我们应该使用resize
在这里插入图片描述

当然如果我们非要使用reserve的话,其实我们可以直接使用push_back接口,想必大家都是可以理解的

6.operator[]和at

这两个接口他与string中的接口也是类似的道理,他们两个也是存在区别的,operator[]接口他比较暴力,是使用断言的。而at中的接口他是比较温柔一点的,他是使用抛异常的方式,异常是可以捕获的

断言的缺陷就是只在debug环境下生效,在release下不生效

7.front和back

同样的与string类似,一个是访问第0个数据,一个是访问最后一个数据。这两个接口完全可以由operator[]运算符重载进行替代,存在一些冗余,但是其实还好。

8.data

data这个接口与string中的c_str接口是十分类似的,data返回的是内部的数组指针。c_str其实也是返回内部的字符串数组指针。

在这里插入图片描述

9.push_back和pop_back

这两个接口就比较简单了,分别为尾插和尾删。比较容易理解
在这里插入图片描述
在这里插入图片描述

需要注意的是,在vector中并没有提供头插头删这两个接口,因为这两个接口的效率是比较低的。一般是很少使用的

10.insert和erase

虽然并没有提供头插头删这两个接口,但是c++提供了insert和erase这个接口,同样的类比的方法,我们可以知道,insert可以在任意一个位置进行插入,erase用于在任意位置删除一个数据
在这里插入图片描述

在这里插入图片描述

我们可以简单的去使用一下

void testvector8()
{
	vector<int> v;
	v.resize(10);
	for (int i = 0; i < 10; i++)
	{
		v[i] = i;
	}
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	//头删
	v.erase(v.begin());
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	//头插
	v.insert(v.begin(), 100);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	//删除第三个位置
	v.erase(v.begin() + 2);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

在这里插入图片描述

假如我们现在想要删除值为5的数据,但是我们发现vector里面并没有find。事实上find在算法库里面是有的。而string中有find的一个原因就是因为string中对find的要求比较多样化,有可能是查找一个子串,有可能是查找某个字符。功能需求比较特殊,而vector是不需要写的。
在这里插入图片描述他需要接收一个迭代器区间,然后在这个区间中找到目标值。

	//删除数值为5的数据,但是不知道5在哪里
	vector<int>::iterator pos = find(v.begin(), v.end(), 5);
	if(pos != v.end())
	{
		v.erase(pos);
	}
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

那么如果一个vector中涉及到很多个5,我们都想删掉,我们该如何操作呢?我们很容易就想当然的以为这样就可以了
在这里插入图片描述结果我们发现程序崩溃了,这里其实涉及到迭代器失效的问题了。我们后序在详细说明

11.assign

如下图所示,assign这个函数的作用主要就是赋值。他会将原来的vector里面的数据给清掉,然后对其进行赋值
在这里插入图片描述

void testvector9()
{
	vector<int> v(10, 0);

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

	v.assign(10, 1);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

在这里插入图片描述

12.swap和clear

如下所示,swap即交换两个vector里面的东西
在这里插入图片描述

clear的作用是清掉vector里面的数据
在这里插入图片描述

五、几道经典例题

1.只出现一次的数字

力扣136:只出现一次的数字

题解:

这道题目是很简单的一道题,直接异或即可,使用C++的做法如下所示

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int val=0;
        for(int i=0;i<nums.size();i++)
        {
            val^=nums[i];
        }
        return val;
    }
};

2.杨辉三角

力扣118:杨辉三角

题解:
这道题题目也是很简单,逻辑上没有任何难度,但是结构上难度较大
如果使用C语言的话,那么他的参数是很复杂的。因为杨辉三角其实就是一个二维数组,二维数组我们就得自己模拟一个二维数组来实现。要开二维数组,得先开一个指针数组,每个指针都要指向一个空间。而我们如果要返回的话,我们就得返回一个指针这个指针数组的指针,这个指针的类型就是int**。

除此之外还有两个输入型参数,一个是数组的行数,一个是数组的列数,由于数组的列数也是变化的,所以其实还是要传一个数组,这个数组我们直接传入的是一个指针,我们在这个指针所指向的空间中开一块数组空间在这里面进行填值。

所以这道题使用C语言结构上是非常之繁琐的
在这里插入图片描述

而如果使用C++的话,那么我们就无需自己开空间了,我们直接使用vector<vector<int>>就可以很方便的开好二维数组。极大的简化了结构上的难度。
在这里插入图片描述

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

3.电话号码的字母组合

力扣17:电话号码的字母组合

题解:

这道题其实确实有些复杂。它是一道典型的多路递归。比二叉树的还要复杂。
我们这样确定思路,题目中已经给出了电话号码组合,而每一个数字又对应着一个字符串的组合。我们先想办法把这些字符串给取出来。取出来以后,我们开始拿这三个字符去与下面的字符进行组合。此处相当于三路递归。为了方便,我们直接写成循环形式。
就这样写成递归的形式,然后就可以顺利的取出所有的字符组合了。
当我们的层数刚好等于电话号码的个数的时候,也就意味着,该结束本层递归了。

class Solution {
    string SArr[10]={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public:
    void Combination(vector<string>& v,string digits,int level,string s)
    {
        if(level==digits.size())
        {
            v.push_back(s);
            return ;
        }

        int num=digits[level]-'0';
        string str=SArr[num];
        for(int i=0;i<str.size();i++)
        {
            Combination(v,digits,level+1,s+str[i]);
        }
    }
    vector<string> letterCombinations(string digits) {
        vector<string> v;
        if(digits.empty())
        {
            return v;
        }
        Combination(v,digits,0,"");
        return v;

    }
};

好了本期内容就到这里了
如果对你有帮助的话,不要忘记点赞加收藏哦!!!

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

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

相关文章

C#月数计算器(主要用于社保、医保缴费月数计算)

1、为什么做这个&#xff1f; 工作中&#xff0c;经常需要计算参保人社保、医保缴费月数&#xff0c;之前都是在Excel中写一个DATEDIF公式&#xff0c;修改单元格中的日期&#xff0c;计算间隔的月数&#xff0c;公式如下&#xff1a; DATEDIF(起始日期, 终止日期, 返回类型) …

QT第二讲

思维导图 完善登录框&#xff0c;当登录成功时&#xff0c;关闭登录界面&#xff0c;跳转到新的界面中 loginscuueed.h #ifndef LOGINSUCCEED_H #define LOGINSUCCEED_H#include <QWidget>namespace Ui { class loginSucceed; }class loginSucceed : public QWidget {…

android数据的储存、文件的储存、SharedPreferences储存、SQLite的基本用法

一、文件的储存 1、将数据储存到文件中 Context类中提供了openfileOutput()方法&#xff0c;用来获取一个文件流&#xff0c;这个方法接收两个参数&#xff0c;第一个参数是文件名&#xff0c;在文件创建的时候使用的就是这个名称&#xff0c;注意这里指定的文件名不可以包含…

白话机器学习笔记(一)学习回归

最小二乘法 定义模型 表达式&#xff1a; f θ ( x ) θ 0 θ 1 x f_\theta(x)\theta_0\theta_1x fθ​(x)θ0​θ1​x &#xff08;常用 θ \theta θ表示未知数、 f θ ( x ) f_\theta(x) fθ​(x)表示含有参数 θ \theta θ并且和变量 x x x相关的函数&#xff09; 目标…

【每日一题】—— D. Prefix Permutation Sums (Codeforces Round 888 (Div. 3))

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

三自由度PUMA机器人非线性控制研究(Matlab代码、Simulink仿真实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f308;4 Matlab代码、Simulink仿真实现 &#x1f4a5;1 概述 针对三自由度PUMA560机器人的控制问题&#xff0c;可以使用三种不同的非线性控制算法&#xff1a;计算扭矩控制、滑模控制和反步…

RL 实践(5)—— 二维滚球环境【REINFORCE Actor-Critic】

本文介绍如何用 REINFORCE 和 Actor-Critic 这两个策略梯度方法解二维滚球问题参考&#xff1a;《动手学强化学习》完整代码下载&#xff1a;6_[Gym Custom] RollingBall (REINFORCE and Actor-Critic) 文章目录 1. 二维滚球环境2. 策略梯度方法2.1 策略学习目标2.2 策略梯度定…

石子合并(区间dp模板)

题目描述&#xff1a; dp分析&#xff1a; 解题代码&#xff1a; #include<iostream> using namespace std;const int N1e36;int f[N][N]; int a[N]; int s[N];int main(){int n;cin>>n;for(int i1;i<n;i){scanf("%d",&s[i]);s[i]s[i-1];//前缀和…

使用fastjson错误

说明&#xff1a;使用fastjson时&#xff0c;对象解析不成功&#xff0c;一直报错&#xff0c;但是json格式没有错&#xff1b; 错误信息&#xff1a;Method threw ‘com.alibaba.fastjson.JSONException’ exception. json数据是正确的 分析&#xff1a;注意看&#xff0c;fa…

【用IDEA基于Scala2.12.17开发Spark 3.4.1 项目】

目录 使用IDEA创建Spark项目设置sbt依赖创建Spark 项目结构新建Scala代码 使用IDEA创建Spark项目 打开IDEA后选址新建项目 选址sbt选项 配置JDK debug 解决方案 相关的依赖下载出问题多的话&#xff0c;可以关闭idea&#xff0c;重启再等等即可。 设置sbt依赖 将sbt…

数据安全问题防不胜防?教你使用铁威马的321原则

无论是勒索病毒的袭击&#xff0c;还是硬件损坏、人为误删等原因造成的数据丢失的意外让我们防不胜防。为此&#xff0c;我们时常建议大家尤其是企业通过【3-2-1备份原则】的数据保护策略来备份数据&#xff0c;以便在数据受损失时&#xff0c;能快速地从备份介质中完整地恢复数…

git相关

gerrit用户指南&#xff1a; 资料&#xff1a;Gerrit 用户指南 gerrit-user-guide 上述有介绍如何review&#xff0c;review并非修改代码之后如何重新提交等操作 jenkins介绍 Jenkins详细教程 - 知乎 一、jenkins是什么&#xff1f; Jenkins是一个开源的、提供友好操作界…

vite的介绍

Vite&#xff08;法语意为 "快速的"&#xff0c;发音 /vit/&#xff0c;发音同 "veet")是一种新型前端构建工具 优势 &#x1f4a1; 极速的服务启动&#xff0c;使用原生 ESM 文件&#xff0c;无需打包 ⚡️ 轻量快速的热重载&#xff0c;始终极快的模块…

Centos7.6安装RocketMQ4.9.2并配置开机自启

1、下载RocketMQ 编译后的压缩包 wget https://dlcdn.apache.org/rocketmq/4.9.2/rocketmq-all-4.9.2-bin-release.zip2、解压 unzip rocketmq-all-4.9.2-bin-release.zip3、进入解压文件夹 cd rocketmq-all-4.9.2/4、编辑配置文件/usr/local/rocketmq/rocketmq-all-4.9.4-…

Spring Boot 拦截器实现:登录验证 统一异常处理 返回数据规范化

学习 Spring 和 servlet 初期&#xff0c;我们在判断用户身份时&#xff0c;都是在每个方法中获取会话、获取对象&#xff0c;这种方式冗余度高&#xff0c;增加代码复杂度&#xff0c;维护成本也高&#xff0c;因此想到可以使用 AOP 来实现一个公共的方法&#xff0c;这个公共…

Android 自定义跳转到系统 Settings Fragment 的 Intent

以跳转到蓝牙控制面板为例&#xff0c;控制面板如图所示&#xff1a; 其 Fragment 所在的位置是&#xff1a; packages/apps/Settings/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java 第一步 要在 Settings的主要 Activity 中定义继承同一个父类…

通达信波段王指标公式_通达信公式

使用说明&#xff1a;1、买点&#xff1a;多空分界线变红后出现波段买为做多信号&#xff0c;中间出现波段卖信号为杂波可以不考虑&#xff0c;再结合逃顶信号进行卖出操作&#xff0c;如果没有出现逃顶信号&#xff0c;则可以等多空分界线变绿后结合波段卖信号综合做出判断。2…

基于proteus的纯模拟病房呼叫系统

摘要&#xff1a;无线的病房呼叫系统将病人的呼叫请求迅速传递给医护者&#xff0c;减少了信息传递的过程性&#xff0c;病人无需等待医护巡查才可求助&#xff0c;增加了便捷性与即时性&#xff1b;此外也减轻一定的工作量&#xff0c;加强服务的效率。本文基于proteus软件进行…

selenium 启动常用浏览器驱动方式

一.启动 Chrome 浏览器 方式一&#xff1a; // 驱动路径的 File File file new File("src/main/resources/drivers/chromedriver-win32.exe"); // 设置系统属性&#xff0c;setProperty() 中两个参数分别是驱动名和 file 绝对路径 System.setProperty("webdr…

如何在Mkdocs里自定义字体(霞鹜文楷)

网站目前在用的字体&#xff1a;霞鹜文楷 想必你可以直观的从我的网站&#xff0c;感受到这款字体的美观程度。 以下是摘录的部分字体官方介绍文档 注意事项 添字请在 Issue #33 反馈&#xff0c;字形调整请在 Issue #14 反馈&#xff0c;不要另开议题&#xff0c;以便于整理。…