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

文章目录

  • 一、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;

    }
};

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

苍穹外卖 Spring Task 来单提醒 催单Apache ECharts day10~11

苍穹外卖-day10 课程内容 Spring Task订单状态定时处理WebSocket来单提醒客户催单 功能实现&#xff1a;订单状态定时处理、来单提醒和客户催单 订单状态定时处理&#xff1a; 来单提醒&#xff1a; 客户催单&#xff1a; 1. Spring Task 1.1 介绍 Spring Task 是Spring框架提供…

CHI协议之DVM操作

本文部分内容参考了CHI的Cache Stashing和DVM操作_谷公子的藏经阁的博客-CSDN博客 □ 注意此处RNF发送的noncopyback data, 只有8byte; □ MN会发送两个SNP给每个RNF, 因为一个SNP&#xff0c;携带的信息不够&#xff1b; □ 如果MN具备将来自同一个源头的non-sync/sync dvmop操…

Python post请求发送的是Form Data的类型

常规的Form Data 大部分的Form Data 可以直接都是可以通过正常的post请求进行提交的 import requestsheaders {自己设置的请求头键: 自己设置的请求头键,Content-Type: 网页接受的数据类型 }form_data {对应的键1&#xff1a;对应的值1,对应的键2&#xff1a;对应的值2, }r…

7、Java入门教程【面向对象】

面向对象是Java编程的核心概念&#xff0c;如果不能充分了解面向对象的思想&#xff0c;那么会给你在实际的项目开发过程中&#xff0c;带来很多业务设计上的困扰。 一、构造器 我们在设计完一个类&#xff0c;在使用这个类去创建对象实例的时候&#xff0c;有些场景是需要对…

css实现鼠标滑动左下角弹框带动画效果

代码 <div classNamekuang></div> css代码 .kuang {height: 500px;width: 400px;// background-color: #fff;position: absolute;z-index: 10;bottom: 0;transform: translateX(-390px)}.kuang:hover {animation: myanimation 3s linear 1;animation-fill-mode:f…

Django学习笔记-视图(views)的使用

Django中可以使用views进行管理&#xff0c;类似于WPF的MVVM的ViewModel层&#xff0c;也相当于MVC架构的模Controller层。 一、基于函数的视图FBV&#xff08;Function-Based View&#xff09; 通过定义一个函数&#xff0c;包含HttpRequest对象作为参数&#xff0c;用来接受…

QT项目代码去UI界面常用开发步骤

QT项目代码去UI界面常用开发步骤 因项目开发需求&#xff0c;领导要求整个QT项目中不要用UI方式来实现界面&#xff0c;这样能保障程序运行稳定性以及代码的逻辑和可读性,先记录具体操作步骤如下&#xff1a; 1、首先我们通过拖控件的方式来实现界面的设计效果&#xff0c…

【动态规划part07】| 70.爬楼梯(进阶)、322.零钱兑换、完全平方数

目录 &#x1f388;LeetCode70. 爬楼梯 &#xff08;进阶&#xff09; &#x1f388;LeetCode322. 零钱兑换 &#x1f388;LeetCode279.完全平方数 &#x1f388;LeetCode70. 爬楼梯 &#xff08;进阶&#xff09; 链接&#xff1a;70.爬楼梯进阶 假设你正在爬楼梯。需要 …

linux 内核总结

注意&#xff1a;队列只是一格以前的称呼&#xff0c;底层是链表 进程 多个cpu&#xff0c;每个cpu都有自己的进程队列 进程分类&#xff1a; 实时进程 与用户交互的进程&#xff0c;需要及时的响应&#xff08;优先级 0~100&#xff09; 普通进程 响应不需要那么及时的…

Python(四十一)流程控制语句——break

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

Docker 全栈体系(六)

Docker 体系&#xff08;高级篇&#xff09; 三、Docker微服务实战 1. 通过IDEA新建一个普通微服务模块 建Module docker_boot 改POM <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" …

微信小程序数字键盘(仿微信转账键盘)

微信小程序input自带数字输入键盘&#xff0c;不过是直接调用的系统键盘&#xff0c;无法个性化。 代码中使用使用了Vant WeappVant UI小程序版&#xff0c;这里就不介绍相关安装说明了&#xff0c;大家自行安装Vant Weapp。 json 用到的组件 {"usingComponents": …

双智城市进展全扫描

本文将综合盘点16座智慧城市基础设施与智能网联汽车协同发展试点城市&#xff08;简称“双智试点城市”&#xff09;建设进展情况。2021年4月和12月&#xff0c;住建部和工信部两次联合印发通知&#xff0c;先后确定北京、上海、广州、武汉、长沙、无锡6个城市为第一批双智试点…

Visio文件编辑查看工具Visio Viewer for Mac

Visio Viewer for Mac可以打开和查看Visio文件&#xff08;.vsd、.vdx和.vsdm文件&#xff09;。它具有简单易用的用户界面&#xff0c;可以快速加载和显示Visio文件。此外&#xff0c;它还支持导出文件为PDF、PNG、JPEG等格式&#xff0c;方便用户进行文件转换和共享。 Visio…

(202307)wonderful-sql:基础查询与排序(task2)

教程链接&#xff1a;Datawhale - 一个热爱学习的社区 知识学习 前提&#xff1a; 上一次任务中提出了本课程的用表&#xff0c;但是我并没有加入这个表&#xff0c;这次学习前先对这个表进行插入。 INSERT INTO product VALUES(0001, T恤衫, 衣服, 1000, 500, 2009-09-20)…

DevOps系列文章 之GitLabCI模板库的流水线

目录结构&#xff0c;jobs目录用于存放作业模板。templates目录用于存放流水线模板。这次使用​​default-pipeline.yml​​作为所有作业的基础模板。 作业模板 作业分为Build、test、codeanalysis、artifactory、deploy部分&#xff0c;在每个作业中配置了rules功能开关&…

【广州华锐互动】AR智慧机房设备巡检系统

AR智慧机房设备巡检系统是一种新型的机房巡检方式&#xff0c;它通过使用增强现实技术将机房设备、环境等信息实时呈现在用户面前&#xff0c;让巡检人员可以更加高效地完成巡检任务。 首先&#xff0c;AR智慧机房设备巡检系统具有极高的智能化程度。该系统可以根据用户设定的…

java代码审计6之ssrf

文章目录 1、java支持的网络请求协议&#xff1a;2、Java 中能发起⽹络请求的类2.1、仅⽀持 HTTP/HTTPS 协议的类2.2、⽀持 sun.net.www.protocol 所有协议的类2.3、审计关键词 3、靶场3.1、漏洞代码13.2、ftp协议读取技巧3.3、无回显之探测内网3.4、无回显之探测文件 之前的文…

区间预测 | MATLAB实现基于QRF随机森林分位数回归多变量时间序列区间预测模型

区间预测 | MATLAB实现基于QRF随机森林分位数回归多变量时间序列区间预测模型 目录 区间预测 | MATLAB实现基于QRF随机森林分位数回归多变量时间序列区间预测模型效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现基于QRF随机森林分位数回归多变量时间序列区间…

Android 耗时分析(adb shell/Studio CPU Profiler/插桩Trace API)

1.adb logcat 查看冷启动时间和Activity显示时间&#xff1a; 过滤Displayed关键字&#xff0c;可看到Activity的显示时间 那上面display后面的是时间是指包含哪些过程的时间呢&#xff1f; 模拟在Application中沉睡1秒操作&#xff0c;冷启动情况下&#xff1a; 从上可知&…