【C++】vector的介绍 | 常见接口的使用

news/2024/10/17 23:34:48/

目录

vector的介绍

常见接口

构造函数

尾插push_back()

vector的遍历

1.用方括号+下标 遍历:

2.调用at()来访问:

3.用迭代器遍历:

4.范围for遍历:

vector的空间修改

vector增删查改

覆盖assign()

查找find()

插入insert()

删除erase()

排序sort()

string与vector的区别


本篇先对vector做一个介绍,然后把常用的接口演示一下。

由于vector的许多接口和string那儿的高度重合,所以这里就不详讲了,主要做一个了解。只要掌握几个最重要的接口,其余的用到时 查文档就好。

vector的介绍

vector是一个类模板。它能够容纳各种类型的对象作为其元素,并且可以动态地调整大小。可以理解为动态数组

它像数组,又不像。

像数组的是:vector也采用的连续存储空间来存储元素。这意味着可以采用下标对vector的元素进行访问,和数组一样高效。

不像数组的是:它的大小是可以动态改变的,而且它的大小会被容器自动处理。

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

当新元素插入时候,这个数组需要被重新分配大小,为了增加存储空间。

其做法是,分配一个新的数组,然后将全部元素移到这个数组。

就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。

vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。

不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。

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

与其它动态序列容器相比(deque、list 、forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。

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

常见接口

包头文件:

#include<vector>

构造函数

两个标了🚩的要记住:

构造函数声明接口说明
vector();(重点)🚩无参构造
vector(size_type n, const value_type& val = value_type());构造并初始化n个val
vector (const vector& x); 🚩拷贝构造
vector (InputIterator first, InputIterator last);使用迭代器进行初始化构造

我们在使用时,要注意vector是一个类模板,要实例化成 具体的类型 来使用:

vector<int> v1;        //创建一个int类型的,空的v1
​
vector<int> v2(5, 1);  //v2含5个1
​
vector<int> v3(v2);   //拷贝构造
​
vector<int> v4(v3.begin(), v3.end());   //用迭代器

尾插push_back()

vector没有头插 / 头删(因为每次都要挪数据,效率太低),也不能用operator+=,

它只能尾插 / 尾删,或者insert/erase。总之,就是一个一个地插入、删除。

#include<vector>
using namespace std;
​
int main()
{vector<int> v1;      v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);return 0;
}

相关接口:尾删pop_back()

vector的遍历

先说明一下:

vector是不能直接用流插入输出的:

因为vector是一个容器,它可以存储多个元素。

而流插入输出操作符<<是用于将一个单独的元素插入到流中的,而不是将整个容器插入到流中。

因此,如果要将vector中的元素输出到流中,需要使用循环逐个输出

现在我们就来看看遍历输出vector的4种方法:

1.用方括号+下标 遍历:

vector是支持operator[]和size()的,因此可以用 方括号+下标 的方式遍历。

#include<iostream>
#include<vector>
using namespace std;
​
int main()
{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] << " ";}return 0;
}

2.调用at()来访问:

at()是像函数一样调用的:

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

这里补充一下at()和operator[]的区别:两者检查越界的方式不一样。

at()是较温和的抛异常:

而operator[]是断言:

3.用迭代器遍历:

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

4.范围for遍历:

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

vector的空间修改

容量空间接口说明
size获取数据个数
capacity获取容量大小
empty判断是否为空
resize (重点)改变vector的size
reserve (重点)改变vector的capacity

这些接口的功能、用法都和string里的类似,就不详讲了。

这里要说下,capacity在vs下和在g++下的增长问题:

vs下capacity是按1.5倍增长的,g++是按2倍增长的。

这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。

来测试下:

vs

#include<vector>
#include<iostream>
using namespace std;
​
int main()
{vector<int> v;   int val = v.capacity();for (size_t i = 0; i < 100; i++){v.push_back(i);if (val != v.capacity()){cout <<"now the capacity is:  "<< v.capacity() << endl;}val = v.capacity();}return 0;
}

g++:

vector增删查改

覆盖assign()

assign:将新内容指定给vector,替换其当前内容,并相应地修改其size。也就是说,起到一个替换、覆盖的作用。

演示:

#include<vector>
#include<iostream>
using namespace std;
​
int main()
{vector<int> v;   v.push_back(1);v.push_back(2);v.push_back(3);for (auto& e : v){cout << e << " ";}cout << endl;
​v.assign(8, 8);for (auto& e : v){cout << e << " ";}return 0;
}

查找find()

其实vector的接口里并未提供find(),不光是vector,list、deque等容器中也没有该函数。

这是因为像vector、list、deque等容器,它们都是从头到尾遍历查找,查找逻辑是相同的。

而在算法库<algorithm>中,find()是一个类模板算法,所有的容器都可以复用,想用时直接套上去用,因此没必要单独实现find()接口了。

注意:

1.要包<algorithm>头文件。

2.迭代器区间,都是左闭右开的 :[ first,last )

3.返回的是迭代器。如果找到了val,返回val的迭代器;如果没找到,返回last,即右边开区间的位置。

示例:

#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
​
int main()
{vector<int> v;   v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);vector<int>::iterator it = find(v.begin(), v.end(), 3);cout << *it << endl;return 0;
}

插入insert()

有了insert(),我们可以实现自由插入了,包括头插。

现在我们就在2的前面插入一个100:

int main()
{vector<int> v;   v.push_back(1);v.push_back(2);v.push_back(3);vector<int>::iterator pos = find(v.begin(), v.end(), 2);   //先找到2的位置posif (pos != v.end()){vector<int>::iterator it = v.insert(pos, 100);     //在pos位置插入100,2被挤到后一个}for (auto& e : v){cout << e << " ";}return 0;
}

删除erase()

示例:

int main()
{vector<int> v;   v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);
​vector<int>::iterator pos = find(v.begin(), v.end(), 2);if (pos != v.end()){v.erase(pos);}for (auto& e : v){cout << e << " ";}return 0;
}

排序sort()

sort()和find()一样,都是在<algorithm>中的类模板算法,可以被不同的容器复用。

➡️排序算法默认排的是升序,例:

#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{vector<int> v;   v.push_back(11);v.push_back(8);v.push_back(3);v.push_back(4);v.push_back(0);v.push_back(4);v.push_back(9);v.push_back(7);
​for (auto e : v){cout << e << " ";}cout << endl;
​sort(v.begin(), v.end());for (auto e : v){cout << e << " ";}cout << endl;return 0;
}

➡️那要怎么排降序呢?

用仿函数(仿函数目前不详讲,现在会用就行),库里面已经写好了,直接拿来用即可。

template <class RandomAccessIterator, class Compare>  
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);   //仿函数Compare comp

两个仿函数,一个叫less(<,升序),一个叫greater(>,降序),两个都是类模板。

注意:在使用时要包<functional>头文件。

示例:

#include<vector>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
​
int main()
{vector<int> v;   v.push_back(11);v.push_back(8);v.push_back(3);v.push_back(4);v.push_back(0);v.push_back(4);v.push_back(9);v.push_back(7);
​for (auto e : v){cout << e << " ";}cout << endl;
​less<int> lt;greater<int> gt;sort(v.begin(), v.end(), lt);   //升序for (auto e : v){cout << e << " ";}cout << endl;
​sort(v.begin(), v.end(), gt);   //降序for (auto e : v){cout << e << " ";}cout << endl;
​return 0;
}

不过,更常见的是用匿名对象来写:

sort(v.begin(), v.end(), greater<int>()); 

string与vector的区别

既然vector是类模板,那vector<char>能代替string吗?

答案当然是不能。

原因:

1.string结尾是有'\0'的,而vector没有。

2.很多string的接口,vector<char>都用不起来,如+=、find、<<、>>、比较大小、to_string等。

所以,几乎不会用vector<char>,一般直接用string。


http://www.ppmy.cn/news/1132035.html

相关文章

算法练习8——有序三元组中的最大值

LeetCode 100088 有序三元组中的最大值 I LeetCode 100086 有序三元组中的最大值 II 给你一个下标从 0 开始的整数数组 nums 。 请你从所有满足 i < j < k 的下标三元组 (i, j, k) 中&#xff0c;找出并返回下标三元组的最大值。如果所有满足条件的三元组的值都是负数&am…

在Qt中,怎么获取到在mainwindow.ui文件中添加的控件

2023年9月30日&#xff0c;周六晚上 假设我在mainwindow.ui中添加了一个名为textEdit的QTextEdit对象 在mainwindow.cpp中&#xff0c;可以通过ui对象来获取到这个控件

《Jetpack Compose从入门到实战》 第二章 了解常用UI组件

目录 常用的基础组件文字组件图片组件按钮组件选择器组件对话框组件进度条组件 常用的布局组件布局Scaffold脚手架 列表 书附代码 Google的图标库 常用的基础组件 文字组件 Composable fun TestText() {Column(modifier Modifier.verticalScroll(state rememberScrollState…

【面试经典150 | 矩阵】有效的数独

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;一次遍历数组 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题涉及到的数据结…

Java中那么多排序方法该怎么选择呢

在 Java 中&#xff0c;有几种常用的排序方法&#xff0c;比如 Arrays.sort 、Collections.sort 和集合自身的 sort 方法。本文将对这三种排序方法的用法、区别和应用场景进行总结。 Arrays.sort Arrays.sort 方法是 Java 中用于对数组进行排序的方法。它可以处理基本类型和对…

Visual Studio Code键盘快捷键大全

Visual Studio Code键盘快捷键大全 前言导航快捷键编辑快捷键多光标快捷键终端快捷键调试快捷键文件管理快捷键Git快捷键代码格式化快捷键代码折叠快捷键工作区快捷键Markdown快捷键Zen模式快捷键窗口管理快捷键重构快捷键IntelliSense快捷键测试快捷键扩展快捷键 前言 欢迎来…

基于Java的汽车票网上预订系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

VSCode 使用 Vue2.0 通用结构模板

安装vscode 官网&#xff1a;https://code.visualstudio.com/ 安装 Vetur 插件&#xff0c;识别 vue 文件 应用商店中搜索 Vetur&#xff0c;点击安装&#xff0c;安装完成之后点击重新加载 新建代码片段 文件 ➡ 首选项 ➡ 用户代码片段 ➡ 点击新建全局代码片段 ➡ 取名…