一、vector介绍
vector
是一个模板类,提供了动态数组的通用功能,具有如下特点:
- 在数组末尾添加元素所需的时间是固定的,即在末尾插入元素的所需时间不随数组大小而异,在末尾删除元素也如此
- 在数组中间添加或删除元素所需的时间与该元素后面的元素个数成正比.
- 存储的元素数是动态的,而 vector 类负责管理内存
vector 是一种动态数组,其结构如下:
常用的vector函数
1. 构造函数和析构函数:
vector<T> v
:创建一个空的 vector 对象,其中 T 是存储的元素类型。vector<T> v(n, value)
:创建一个包含 n 个初始值为 value 的元素的 vector 对象。vector<T> v(begin, end)
:通过迭代器范围 [begin, end) 创建一个 vector 对象。~vector()
:析构函数,用于释放内存。
2. 元素访问:
v[i]
:使用下标索引访问第 i 个元素。v.at(i)
:使用下标索引访问第 i 个元素,并进行边界检查。v.front()
:获取第一个元素。v.back()
:获取最后一个元素。v.data()
:返回指向底层数组的指针。
3. 容量操作:
v.empty()
:检查 vector 是否为空。v.size()
:返回当前 vector 中元素的数量。v.capacity()
:返回 vector 当前能够容纳的元素数量。v.reserve(n)
:请求 vector 预留至少能容纳 n 个元素的内存空间。v.resize(n)
:将 vector 的大小调整为 n,并在需要时插入或删除元素。
4. 修改容器:
v.push_back(value)
:在 vector 的末尾插入一个元素。v.pop_back()
:删除 vector 的最后一个元素。v.insert(position, value)
:在指定位置插入一个元素。v.erase(position)
:删除指定位置的元素。v.clear()
:删除 vector 中的所有元素。
5. 迭代器操作:
v.begin()
:返回指向第一个元素的迭代器。v.end()
:返回指向最后一个元素的下一个位置的迭代器。v.rbegin()
:返回指向最后一个元素的逆向迭代器。v.rend()
:返回指向第一个元素的前一个位置的逆向迭代器。
二、vector具体用法
1.创建vector
首先,使用vector时需包含头文件:
#include <vector>
vector本质是类模板,可以存储任何类型的数据。数组在声明前需要加上数据类型,而vector则通过模板参量设定类型。
vector<int> arr1; //一个空数组
vector<int> arr2 {1, 2, 3, 4, 5}; //包含1、2、3、4、5五个变量
vector<int> arr3(4); //开辟4个空间,值默认为0
vector<int> arr4(5, 3); //5个值为3的数组
vector<int> arr5(arr4); //将arr4的所有值复制进去,和arr4一样
vector<int> arr6(arr4.begin(), arr4.end()); //将arr4的值从头开始到尾复制
vector<int> arr7(arr4.rbegin(), arr4.rend()); //将arr4的值从尾到头复制
2.遍历vector
(1).迭代器访问
通过迭代器访问从begin()到end(),需要定义iterator,当然可以用auto替代。
begin()表示第一个元素,而end()不是最后一个元素,end()是返回指向最后一个元素的下一个位置的迭代器。
//迭代器:vector<int>::iterator
for (vector<int>::iterator it = arr.begin(); it != arr.end(); it++)
{
cout << *it << endl;
}
//迭代器:vector<int>::reverse_iterator
for (vector<int>::reverse_iterator it = arr.rbegin(); it != arr.rend(); it++)
{
cout << *it << endl;
}
(2).下标访问
和数组类似,从下标0开始遍历,而不到size的大小。
for (int i = 0; i < arr.size(); i++)
{
cout << arr[i] << endl;
}
(3).范围for循环
C++11 引入了一种称为“范围for循环”(Range-based for loop)的特性,它提供了一种更简洁的遍历容器元素的方式。范围for循环可以用于迭代数组、容器和其他支持迭代器的数据结构。
vector<int> numbers = {1, 2, 3, 4, 5};
for (int num : numbers) {
cout << num << endl; // 输出当前元素的值
}
在上述代码中,num 是范围for循环内部新声明的变量,它会依次被赋值为 numbers 容器中的每个元素值,并在循环体内输出。
3.vector容量和大小
-
size
表示当前有多少个元素,capacity
是可容纳的大小。因为vector
是顺序存储的,那么和数组一样,有一个初始容量,在vector
里就是capacity
。capacity
必然大于等于size
,每次扩容时会改变,具体大小和vector
底层实现机制有关。 -
max_size
是可存储的最大容量,和实际的编译器、系统有关,使用的比较少。 -
empty
很好理解,判断vector
是否为空,其实就是判断size
是否等于0
。定义vector
时设定了大小、resize
修改大小等操作,vector
都不为空;clear
后size=0
,那么empty
判断就为空。 -
resize
改变size
的大小,而reserve
改变capacity
的大小,shrink_to_fit
减小capacity
到size
。
vector<int> arr;
arr.resize(4);
arr.reserve(6);
cout << arr.size() << " " << arr.capacity() << endl;
cout << "##########################" << endl;
arr.shrink_to_fit();
cout << arr.size() << " " << arr.capacity() << endl;
4.vector常用算法
(1).push_back、pop_back 和 emplace_back
push_back()、pop_back() 和 emplace_back() 是 std::vector 容器提供的三个用于操作尾部元素的函数。
- push_back( ):
函数用于在 std::vector 的尾部添加一个元素。它接受一个参数,该参数是要添加到容器中的元素的值副本或移动。
std::vector<int> vec = {1, 2, 3};
vec.push_back(4);
//push_back() 将整数值 4 添加到了 vec 的尾部。
- pop_back( ):
函数用于删除 std::vector 的尾部元素。它不接受任何参数。
std::vector<int> vec = {1, 2, 3, 4, 5};
vec.pop_back();
//pop_back() 将删除 vec 的最后一个元素,即值为 5 的元素。
- emplace_back( ):
函数用于在 std::vector 的尾部就地构造一个新元素。它接受任意数量的参数,并使用这些参数来构造一个新元素。
struct Point {
int x;
int y;
Point(int px, int py) : x(px), y(py) {}
};
std::vector<Point> points;
points.emplace_back(10, 20);
//emplace_back() 构造了一个 Point对象并将其添加到 points 的尾部。
//通过传递参数10和20,我们可以在函数内部直接构造 Point 对象,而无需先创建一个临时对象。
push_back将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);
emplace_back 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。所以emplace_back的速度更快。
(2).insert 和 emplace
insert有三种用法:
- 在指定位置插入值为val的元素。
//在arr的头部插入值为10的元素
vector<int> arr;
arr.insert(arr.begin(), 10);
- 在指定位置插入n个值为val的元素
//从arr的头部开始,连续插入3个值为10的元素
vector<int> arr;
arr.insert(arr.begin(), 3, 10);
- 在指定位置插入区间[start, end]的所有元素
//从arr的头部开始,连续插入arrs区间[begin, end]的所有元素
vector<int> arr;
vector<int> arrs = { 1, 2, 3, 4, 5 };
arr.insert(arr.begin(), arrs.begin(), arrs.end());
emplace
emplace和insert同为插入元素,不过emplace只能插入一个元素,在指定位置之前就地构造一个新元素。它接受插入位置的迭代器和构造新元素所需的参数。
struct Point {
int x;
int y;
Point(int px, int py) : x(px), y(py) {}
};
std::vector<Point> points;
points.emplace(points.begin(), 10, 20);
// 在起始位置插入一个新的 Point 对象
这些函数将在指定位置之前插入元素,因此会导致插入位置之后的元素向后移动。插入操作可能涉及内存重新分配和元素复制/移动,因此可能会对性能产生影响。
(3).erase
erase
通过迭代器删除某个或某个范围的元素,并返回下一个元素的迭代器。
vector<int> arr{1, 2, 3, 4, 5};
//删除arr开头往后偏移两个位置的元素,即arr的第三个元素,3
arr.erase(arr.begin() + 2);
//删除arr.begin()到arr.begin()+2之间的元素,删除两个;
//删除范围:[vec.begin(), vec.begin() + 2)
arr.erase(arr.begin(), arr.begin() + 2);
(4).assign
assign修改vector,和insert操作类似,不过insert是从尾部插入,而assign则将整个vector改变。
- 将整个vector修改为n个值为val的容器:
std::vector<int> vec = {1, 2, 3};
vec.assign(5, 10); // 将容器中的元素替换为 5 个值为 10 的元素
- 将整个vector修改为某个容器[start, end]范围内的元素:
//将arr修改为范围[arrs.begin, arrs.end]内的元素
vector<int> arr = {5, 4, 3, 2, 1};
vector<int> arrs = { 1, 2, 3, 4, 5 };
arr.assign(arrs.begin(), arrs.end());
- 用数组的值进行范围修改
//将arr替换为数组arrs
vector<int> arr = {5, 4, 3, 2, 1};
int arrs[5] = { 1, 2, 3, 4, 5 };
arr.assign(arrs, arrs + 5);
(5).swap 和 clear
swap:
swap将两个vector进行交换。
vector<int> arr = {5, 4, 3, 2, 1};
vector<int> arrs = { 1, 2, 3, 4, 5 };
arr.swap(arrs);
clear:
clear清空整个vector,size变为0,但空间仍然存在。
arr.clear();
5.vector二维操作
二维vector其实就是嵌套定义vector,那么对其进行操作我们可以从嵌套的vector得到单层的vector,就可以调用其方法了。
定义:
vector<vector<int>> arr; //定义一个空的二维vector
vector<vector<int>> arr(5, vector<int>(3, 1));
//定义一个5行3列值全为1的二维vector
访问:
和二维数组一样通过[ ] [ ]
访问即可:
for (int i = 0; i < arr.size(); i++)
{
for (int j = 0; j < arr[0].size(); j++)//注意如果arr为空不可直接arr[0]
{
cout << arr[i][j] << endl;
}
}
或者用范围for
:
#include <iostream>
#include <vector>
int main() {
std::vector<std::vector<int>> matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// 打印矩阵
for (const auto& row : matrix) {
for (int num : row) {
std::cout << num << " ";
}
std::cout << std::endl;
}
return 0;
}
vector扩容原理
std::vector 是一个动态数组,其扩容原理可以通过以下步骤来解释:
- 当向
std::vector
插入新元素时,如果当前容量不足以容纳新元素,即size()
已经等于capacity()
,需要进行扩容。 - 扩容时,
std::vector
会分配一块更大的内存空间来存储元素,通常是当前容量的两倍或某个固定增长因子。 - 原始数据将被复制到新的内存空间中。
- 原始内存空间将被释放。
需要注意的是,扩容是一个相对耗费时间的操作,因为需要重新分配内存并复制数据。为了避免频繁的扩容,如果事先已知 std::vector 需要存储大量元素,可以通过前置调用 reserve() 函数来预分配足够的空间。