十一、vector 类

news2024/11/25 11:28:02

Ⅰ . vector 的介绍和使用

01 vector 的介绍

vector 的文档介绍:vector

① vector 是表示可变大小数组的序列容器,既像数组,又不像数组

像体现在:同样采用连续存储空间存储元素,可以使用下标访问元素

不像体现在:大小是可以动态改变的

② 本质上来说,vector 使用动态分配数组来存储它的元素

当新元素插入时,为了增加存储空间,这个数组就需要被重新分配大小。具体做法是分配一个新的数组,然后将全部元素转移到这个新的数组。就时间而言,这是一个相对代价较高的任务,因为每当一个新的元素加入后,vector 并不会每次都重新分配大小。

③ vector 分配空间策略:vector 会分配一些额外的空间以适应可能的增长,因此存储空间会比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于末尾插入一个元素时是在常数时间的复杂度完成的。

因此,vector 占用了更多的存储空间,为了获得管理存储空间的能力,并且有一种有效的方式去动态增长。

④ 与其他动态序列容器相比:

vector 在访问元素时更加高效,末尾添加和删除元素相对高效

但是对于其他位置不在末尾的插入和删除操作,效率更低

vector 的底层是一个动态的顺序表

02 vector 的初始化

#include <vector>
using namespace std;   

① 无参构造:

vector<int> v1;   // 无参构造,创建一个int类型的,空的vector对象

② 构造并初始化:

vector<int> v2(10, 1);   // 10个1

③ 迭代器区间初始化:

vector<int> v3(v2.begin(), v2.end());

④ 拷贝构造:

vector<int> v4(v2);

还可以不全部拷贝:

vector<int> v3(++v2.begin(), --v2.end());

对于迭代器区间初始化,它这里的 InputInerator 不一定是 vector Inerator。

它是一个模板,所以你传的是谁的迭代器,它就可以实例化出谁的迭代器

有时候我们可以这样初始化:

string s("hello world");
vector<char> v5(s.begin(), s.end());

03 调试观察

#include<iostream>
#include<vector>
#include<string>
using namespace std;

void vector_test1()
{
	vector<int> v1;
	vector<int> v2(10, 1);
	vector<int> v3(v2.begin(), v2.end());
	vector<int> v4(v2);

	string s("hello world");
	vector<char> v5(s.begin(), s.end());
}

int main()
{
	vector_test1();

	return 0;
}

具体如下:

04 string 和 vector 的区别

string 和 vector 有什么区别呢?刚才通过监视观察,感觉 vector char 已经很像 string 了。

能不能让 vector char 去替代 string 呢?这合适吗?

答案是不能,因为 vector char 没有 \0 ,而 string 结尾有 \0

那我直接给 vector char 后面手动装一个 \0 行嘛?

你要硬是想这么玩,也不是不可以

但他们两个体系不一样,vector 是顺序表,存的是任意类型,是针对可动态增长的数组的。

而 string 就只是字符串,我随便举两个例子:

① vector 支持正常的增删查改,但是不支持 += (本身也没必要+=),也不支持比较大小。

② vector 也没有 c_str 这些东西,因为 string 作为字符串专用的类,能提供专有的接口(比如 +=,find),所以这就是 string 存在的意义。

05 关于 vector 的析构、拷贝构造和赋值构造

至于析构函数,一般情况下我们不需要管,因为它会自动调用。

拷贝构造和赋值构造,vector 的拷贝构造和赋值其实就是深拷贝。

这些我们放在 vector 模拟实现的章节里详细探讨。

Ⅱ . vector 的遍历

01 push_back()

vector 不能使用 operator+= 

string 能用 += 主要是 string 不仅可以尾插一个字符还可以追加一个字符串。

但是 vector 就只支持一个一个数据的插入和删除,push_back 和 pop_back

我们发现,vector 和 string 一样,是没有提供 push_front 和 pop_front 的,因为挪动数据效率低。

如果你硬是要去头插头删,可以用 insert 和 erase 去操作

举个例子:

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

02 下标 + 方括号遍历

vector 是连续的空间,又支持 operator[] 和 size() ,所以可以用下标+方括号遍历。

看代码:

void vector_Traversal_test() 
{
	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;
}

运行结果如下:

当然也可以修改:

void vector_Traversal_test() 
{
	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++)
	{
		v[i] += 1;
		cout << v[i] << " ";
	}
	cout << endl;
}

运行结果如下:

03 访问 vector 的 at()

at() 和 operator[] 一样,也是用来访问 vector 的,但是 at() 会进行边界检查。

代码演示:

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

	cout << v.at(0) << endl;
	cout << v.at(3) << endl;
}

运行结果如下:

注意:operator[] 会断言检查越界,at() 会抛异常

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

	cout << v.at(0) << endl;
	cout << v.at(3) << endl;
	cout << v.at(4) << endl;
}

04 使用迭代器遍历

代码演示:

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

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

运行结果如下:

05 范围 for

vector 支持迭代器,也就支持范围 for,这个我们在模拟实现 string 的时候已经验证过了。

范围 for 的本质就是编译器在编译时自动替换成迭代器,这里也一样。

代码演示:

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

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

	for (auto& e : v)
	{
		e += 10;
		cout << e << " ";
	}
	cout << endl;
}

运行结果如下:

Ⅲ . vector 空间

01 获取数据个数的 size()

void vector_test3() 
{
	vector<int> v(6, 6);        // 生成6个6
	cout << v.size() << endl;
}

运行结果如下:

02 获取 vector 最大存储的 max_size()

void vector_test4()
{
	vector<int> v(6, 6);        
	cout << v.max_size() << endl;
}

运行结果如下:

03 改变 vector 容量的 reserve()

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

	v.reserve(100);
}

调试结果如下:仅扩容

reserve 会扩容,但是不会影响数据个数。[capacity] 4  → [capacity]100

04 改变 vector 大小的 resize()

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

	v.resize(100);
}

调试结果如下:扩容 + 初始化

string 的 resize 如果不指定 "填充值" ,默认给的是 \0

而 vector 的 resize 如果不指定,默认给的是其对应类型的缺省值作为 "填充值"

这里是 int 就是 0,如果是指针,对应的缺省值就是空指针。

提供特定的值:

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

	v.resize(100, 1);
}

调试结果如下:

注意:如果开的数据比之前更小,还会删除数据!

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

	v.resize(2);
}

调试结果如下:

这里虽然大小变成 2,数据也只有 [0]1 和 [1]2 了,但是容量仍然为 4

05 vector 空间增长问题

① capacity 的代码在 VS 和 g++下分别运行会发现:VS下 capacity 是按 1.5 倍增长的,而 g++ 下 capacity 是按 2 倍增长的。 这个问题经常会考察,不要固化的认为,顺序表增容都是2倍,具体增长多少是根据具体的需求定义的。VS 是 PJ 版本 STL,g++ 是 SGI 版本 STL。

② reserve 只负责开辟空间,如果确定知道需要用多少空间,reserve 可以缓解 vector 增容的代价缺陷问题。

③ resize 在开空间的同时还会进行初始化,影响 size

测试:

// vector::capacity
#include <iostream>
#include <vector>
 
int main()
{
	size_t sz;
	std::vector<int> foo;
	sz = foo.capacity();
	std::cout << "making foo grow:\n";
	for (int i = 0; i < 100; ++i) {
		foo.push_back(i);
		if (sz != foo.capacity()) {
			sz = foo.capacity();
			std::cout << "capacity changed: " << sz << '\n';
		}
	}
}

 VS运行结果如下:

making foo grow :
capacity changed : 1
capacity changed : 2
capacity changed : 3
capacity changed : 4
capacity changed : 6
capacity changed : 9
capacity changed : 13
capacity changed : 19
capacity changed : 28
capacity changed : 42
capacity changed : 63
capacity changed : 94
capacity changed : 141

g++ 运行结果如下:

making foo grow :
capacity changed : 1
capacity changed : 2
capacity changed : 4
capacity changed : 8
capacity changed : 16
capacity changed : 32
capacity changed : 64
capacity changed : 128

Ⅳ . vector 增删查改

01 pop_back() 尾删

代码实现:

void vector_test7() 
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	for (auto e : v) cout << e << " "; cout << endl;

	v.pop_back();   // 尾删:3
	for (auto e : v) cout << e << " "; cout << endl;

	v.pop_back();   // 尾删:2
	for (auto e : v) cout << e << " "; cout << endl;
}

运行结果如下:

02 assign() 赋值

用 n 个 value 覆盖:

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

	v.assign(10, 5);   // 原来是1到4,现在改成10个5
}

调试结果如下:

03 为什么 vector 不提供 find 接口

string、map、set 都有 find() 用,凭什么 vector 和 list 没有

其实,我们应该考虑的是 —— 为什么 string、map、set 能有 find 操作。

而 vector 之所以不提供 find ,是因为如果去查找元素效率就会是 O(n) ...

凭什么不让我 vector 用 find() ,我偏要用!

可以的,"algorithm库" 里有通用的 find 操作,我们来一睹其芳容 ——

#include <algorithm>

该 find 内部是从 begin 到 end 进行一次遍历,其复杂度是 O(n) 

值得一提的是,在C++中,凡是使用迭代器区间,都是左闭右开的

再去思考 map、set 为什么有 find() 通过迭代器从头到尾遍历 map 与 set 时,

得到的结果是按 key 排序的结果,而不是插入时的顺序,所以这两个容器没有 push_back 操作。

其实,插入到 map 与 set 中的元素会被组织到一颗红黑树上,红黑树是一颗平衡二叉树,

平衡二叉树是一颗二叉排序树,对一颗二叉排序树的查找有点像二分查找,复杂度是 ,

由于 map 与 set 的数据结构能有更快的查找方法,所以在容器内提供了 find 方法

04 通用查找 find()

如果非要在 vector 里用 find() ,我们可以用通用 find。

找到了:返回一个迭代器区间那个值的位置;

没找到:返回的是 last ,即右边开区间的位置。

代码演示:

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

	vector<int>::iterator ret = find(v.begin(), v.end(), 3);
	// auto ret = find(v.begin(), v.end(), 3);

	if (ret != v.end()) 
    {
		cout << "找到了" << endl;
	}
}

05 insert() 插入

比如我们刚才用通用 find 找到了 3 的位置,

我们想在这个位置前面插入一个数据,就可以使用 insert() 插入。

在 3 前面插入一个 30:

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

	vector<int>::iterator ret = find(v.begin(), v.end(), 3);
	// auto ret = find(v.begin(), v.end(), 3);

	if (ret != v.end()) 
	{
		cout << "找到了" << endl;
		v.insert(ret, 30);
	}
	for (auto e : v) cout << e << " "; cout << endl;
}

运行结果如下:

虽然没有 vector 没有提供 push_front,但是我们也可以用 insert 去头插

代码演示:

void vector_test10() 
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	for (auto e : v) cout << e << " "; cout << endl;

	v.insert(v.begin(), -1);   // 在起始位置插入一个-1

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

运行结果如下:

06 erase() 删除

我们我们想删除数据,我们就可以用 erase 去删除

代码演示:

void vector_test11() 
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	for (auto e : v) cout << e << " "; cout << endl;

	vector<int>::iterator pos = find(v.begin(), v.end(), 3);
	if (pos != v.end()) 
	{   // 判断pos是否存在
		v.erase(pos);       // 删除pos
	}

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

运行结果如下:

如果没有判断会怎么样?

void vector_test11() 
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	for (auto e : v) cout << e << " "; cout << endl;

	vector<int>::iterator pos = find(v.begin(), v.end(), 3);
	v.erase(pos);       


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

运行结果如下:

如果要删除的目标存在,不会怎么样。

怕的就是要删的目标不存在!比如我要删个 5,但是 vector 里只有 1,2,3,4 。

vector<int>::iterator pos = find(v.begin(), v.end(), 5);
v.erase(pos);

如果有了判断,就不会翻车了,如果待删目标不存在,就不会去走 erase()  

因为 pos 如果找不到就会等于 end() 上的值,我们利用这一点进行 if 判断

	vector<int>::iterator pos = find(v.begin(), v.end(), 5);
	if (pos != v.end()) 
	{   // 检查!
		v.erase(pos);
	}

07 clear() 清空数据

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

	printf("清空前:");
	for (auto e : v) cout << e << " "; cout << endl;

	v.clear();
	printf("清空后:");
	for (auto e : v) cout << e << " "; cout << endl;
}

运行结果如下:

clear只是把数据清了,但容量还在

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

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

相关文章

ffmpeg -- 常用口令

文章目录 1.视频格式转换2.设置比特率3.设置帧率4.强制让输入视频帧率为1&#xff0c;输出视频帧率为245.长视频截短6.自动分割视频的bash脚本7.每一帧都保存成图片 1.视频格式转换 ffmpeg -i input.avi output.mp42.设置比特率 ffmpeg -i input.avi -b:v 64k -bufsize 64k o…

Kuboard v3安装手册

1、Kuboard v3安装 docker pull swr.cn-east-2.myhuaweicloud.com/kuboard/kuboard:v3 #启动kuboard容器 docker run -d \ --restartunless-stopped \ --namekuboard \ -p 8003:80/tcp \ -p 30081:10081/tcp \ -e KUBOARD_ENDPOINT"http://10.111.13.2:8003&q…

智能分析,安全无忧:AI视频分析技术在安全生产中的深度应用

在当今快速发展的科技时代&#xff0c;视频智能分析技术&#xff08;Intelligent Video Analysis&#xff0c;简称IV&#xff09;已经成为提升安全生产水平的重要手段。这一技术通过计算机图像视觉分析技术&#xff0c;实现了对场景中目标的自动识别和追踪&#xff0c;有效提升…

计算机毕业设计选题推荐-玩具租赁系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

网络规划设计师-(3)数据通信系统模型

数据通信系统模型是怎么组成的&#xff1f; 数据通信系统模型是由以下几个组成部分组成&#xff1a; 发送方&#xff08;Sender&#xff09;&#xff1a;发送方是数据通信系统中起始数据源的设备或程序。它将原始数据转化为适合传输的格式&#xff0c;并将数据发送到通信信道中…

企业管理咨询公司谈Poka-Yoke的三种方法

这听起来像是你玩的游戏&#xff0c;但 poka-yoke 这个词实际上是一个日语术语&#xff0c;它的意思是“防错”。作为一种质量工具&#xff0c;其背后的理念是每个人都会犯错&#xff0c;因为我们都是人。Poka-yoke 要么消除&#xff0c;要么至少大大减少了我们犯错的机会。此操…

中国进出口贸易公司 | 地毯进口有哪些要求 | 箱讯科技

地毯进口有哪些注意事项&#xff1f;根据制作方法、材质等方面的差异&#xff0c;地毯产品在进口申报时&#xff0c;其申报税号各有不同&#xff1b;还应关注地毯产品的质量安全及卫生环保情况&#xff0c;确保其符合中国相关强制性标准的要求。具体介绍如下。 进口地毯产品申报…

记一次 .NET某智慧出行系统 CPU爆高分析

一&#xff1a;背景 1. 讲故事 前些天有位朋友找到我&#xff0c;说他们的系统出现了CPU 100%的情况&#xff0c;让你帮忙看一下怎么回事&#xff1f;dump也拿到了&#xff0c;本想着这种情况让他多抓几个&#xff0c;既然有了就拿现有的分析吧。 二&#xff1a;WinDbg 分析…

MySQL笔记-基础篇(二):多表查询

​ 博客主页: 南来_北往 系列专栏&#xff1a;Spring Boot实战 前言 MySQL的多表查询是一项非常实用的数据库操作技术&#xff0c;它能够通过关联不同表中的数据来提供更加丰富和准确的信息。在实际应用中&#xff0c;数据通常不是孤立存在的&#xff0c;而是分布在多个…

怎么用云手机进行TikTok矩阵运营

TikTok作为炙手可热的社交媒体巨头&#xff0c;已经吸引了亿万用户的目光。随着科技的飞速发展&#xff0c;云手机的出现为TikTok矩阵运营注入了新的活力。本文将深入探讨云手机在TikTok矩阵运营中的实际应用&#xff0c;并分享一系列高效策略与技巧。 &#xff08;1&#xff0…

《暗黑破坏神 IV》是什么类型的游戏,苹果电脑能玩暗黑破坏神吗 crossover玩暗黑4

《暗黑破坏神 IV》&#xff08;Diablo IV&#xff09;是由暴雪娱乐开发的一款动作角色扮演游戏&#xff08;Action RPG&#xff09;&#xff0c;是广受欢迎的《暗黑破坏神》系列的最新作品。暗黑破坏神4拥有出色的游戏画面、音效和丰富的游戏玩法&#xff0c;非常值得玩家们去尝…

Sqli-labs靶场详解(二)

一.sqli-labs靶场(15~17) 15.less15 第15关发现联合查询和报错注入无效,使用and探测时发现有两个页面,于是判断是盲注中的布尔盲注 猜解库名长度 or (length(database()))8-- gwe 利用ASCI码猜解当前数据库名称: or(ascii(substr(database(),1,1)))115-- gwe返回正常&#xf…

Linux内核机制开发 - DEFINE_PER_CPU和DECLARE_PER_CPU

By: fulinux E-mail: fulinuxsina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅&#xff01; 你的喜欢就是我写作的动力&#xff01; 目录 1. DEFINE_PER_CPU和DECLARE_PER_CPU1. 1. DEFINE_PER_CPU1.2. DECLARE_PER_CPU 2. 区别2.1. 定义与声明:2.2. 使…

mac专业图表绘制软件:OmniGraffle Pro for mac 激活版

OmniGraffle Pro for Mac 是一款功能强大的图表、流程图、快速页面布局、网站线框和图形设计绘制软件。它具备保持线条与形状连接的功能、有效的样式工具&#xff0c;以及简单易用的组织图表能力&#xff0c;能够快速帮助用户制作出色的图形文稿。OmniGraffle Pro 适用于制作从…

“绿色积分引领新风尚:‘我店‘平台重塑消费市场格局“

在数字化浪潮席卷全球的今天&#xff0c;消费市场正经历一场前所未有的深刻变革与重塑。传统商业模式在数字化的大潮中面临严峻挑战&#xff0c;而“我店”平台凭借其开创性的绿色积分体系&#xff0c;犹如破晓之光&#xff0c;照亮了行业前行的道路&#xff0c;迅速成为市场瞩…

visual studio 2019 以及 TwinCAT3 安装教程

环境&#xff1a; 系统&#xff1a;windows11系统&#xff0c;CPU&#xff1a;Inter I5 一.安装visual studio 这里安装版本不要太高&#xff0c;建议2019或者2017的profession就好。如果你电脑上已经有了一个更高的版本&#xff0c;也不用去卸载高版本再安装低版本&#…

电子纸打造智能、自动化、绿色的工作流程

电子纸打造智能、自动化、绿色的工作流程 RFID技术最早在1940年代问世&#xff0c;1980年开始商业化使用。直到现在RFID&#xff08;无线射频识别&#xff09;技术已经深入到我们生活的方方面面。特别是在工业生产、物流运输等领域&#xff0c;RFID技术发挥着越来越重要的作用…

达梦数据库:无法解析的成员访问表达式[GROUP_CONCAT]

目录 异常原因解决1、LISTAGG()&#xff08;推荐&#xff09;2、WM_CONCAT() 异常 原因 达梦数据库没有GROUP_CONCAT()函数 解决 1、LISTAGG()&#xff08;推荐&#xff09; 2、WM_CONCAT() Java结果集映射后&#xff0c;会是一个地址值

北斗三号5G遥测终端机系统在水库大坝安全监测应用

一、概述 我国现有水库大坝9.8万余座&#xff0c;是世界上拥有水库大坝最多的国家。这些水库大坝在防洪、发电、供水、灌溉等方面发挥巨大效益的同时&#xff0c;所存在的安全风险不容忽视。大坝安全监测是大坝安全管理的重要内容&#xff0c;是控制大坝风险的重要措施。大坝安…

java面向对象--继承(构造方法的访问特点)

一、引言 &#xff08; 简单介绍构造方法的特点) 在 Java 中&#xff0c;面向对象编程中的继承机制允许子类继承父类的属性和方法。当涉及到构造方法时&#xff0c;有一些特定的规则需要遵循。以下是在继承中构造方法访问的一些关键特点&#xff1a; 构造方法的访问特点1、构造…