vector 的技术文档文档链接

1 vector 的概念

vector 是 STL 容器的一种,vector 既可以用来保存内置类型数据(char,short,int等),也可以用来保存自定义类型数据(对象),它是顺序表的一种实现,因此,可以将它视为一个一维数组,它还可以通过嵌套的方式来实现二维数组

想要使用 vector,就需要包含头文件 vector 并展开命名空间 std

#include <vector>
using namespace std;

接下来的说明中,主要以一维 vector (一维数组)为主

2 vector 的常用接口

2.1 vector 的构造函数

vector 的构造函数主要有以下几种:

在这里插入图片描述

在使用构造函数构造 vector 时,要遵循如下的语法:

vector<类型名> 对象名;

构造 vector 的方法主要有:

(1)使用空间配置器进行构造,这个方式不太常用,因此在这里不做介绍

(2)使用 n 个 val 进行构造

用这种方式构造时,val 为用户给定的值,如果未给定,则它会使用缺省值(默认值),默认值为当前 vector 存储的元素对应的类型的默认值,比如当前的 vector 要存储 int 类型的值,那么它的默认值是 0,如果要存储 string,那么它的默认值是空字符串

vector<int> v(5, 1); //n = 5, val = 1

在这里插入图片描述

(3)使用其它容器的迭代器来确定区间,用区间内的元素来进行构造

用这种方式构造的时候,要保证用于构造 vector 的容器中所存储的数据的类型要和 vector 中将要存储的数据的类型一致,也就是说,如果 vector 中要存储 int 类型的数据,是不可以用 string 中存储的元素来进行构造的,因为 string 中存储的是字符

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

在这里插入图片描述

(4)使用一个已经存在的 vector 来进行构造(拷贝构造)

用这种方式构造的时候要保证两个 vector 存储的数据的类型是一致的

vector<int> v(5, 1);
vector<int> v2(v);

在这里插入图片描述

(5)构造一个空的 vector

如果在定义 vector 时,对象名后不加括号,就会构造出一个空的 vector

vector<int> v3;

在这里插入图片描述

2.2 vector 的遍历

在遍历 vector 时,会使用三种方式,分别是 下标,迭代器和范围 for:

(1)使用下标遍历

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	for (size_t i = 0;i < v1.size();++i)
	{
		cout << v1[i] << " ";
	}
	return 0;
}

运行结果:
在这里插入图片描述
在这里插入图片描述

(2)使用迭代器遍历

在使用迭代器遍历时,会使用到以下几个接口:

接口名称 作用 返回值类型
begin() 返回指向第一个元素的迭代器 普通对象返回 iterator,const 对象返回 const_iterator
end() 返回指向最后一个元素的下一个位置的迭代器 普通对象返回 iterator,const 对象返回 const_iterator
rbegin() 返回指向最后一个元素的迭代器 普通对象返回 reverse_iterator,const 对象返回 const_reverse_iterator
rend() 返回指向第一个元素的前一个位置的迭代器 普通对象返回 reverse_iterator,const 对象返回 const_reverse_iterator

在这里插入图片描述
vector 的迭代器属于 随机迭代器,可以进行++,- -,+,- 操作,使用迭代器可以分别进行 正向遍历和反向遍历

正向遍历:

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	vector<int>::iterator it = v1.begin();
	while (it != v1.end())
	{
		cout << *it << " ";
		it++;
	}
	return 0;
}

在这里插入图片描述

反向遍历:

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	vector<int>::reverse_iterator rit = v1.rbegin();
	while (rit != v1.rend())
	{
		cout << *rit << " ";
		rit++;
	}
	return 0;
}

在这里插入图片描述

(3)范围 for 与 auto 遍历

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	for (auto e : v1)
	{
		cout << e << " ";
	}
	return 0;
}

在这里插入图片描述

在 auto 后加上 & 就是一个引用类型由 auto& 修饰的变量 e 就是每一个元素的别名,此时对 e 进行修改会影响结果,但不会影响 vector 中元素的值

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	for (auto& e : v1)
	{
		cout << e + 10 << " ";
	}
	return 0;
}

在这里插入图片描述

2.3 vector 的空间管理

在对 vector 进行空间管理时,经常会使用到以下几个接口:

接口名称 作用 返回值类型
size() 返回 vector 中存储的有效元素个数 size_t (无符号整形)
capacity() 返回 vector 的真实容量大小 size_t (无符号整形)
resize() 增加或缩减有效元素个数 void
empty() 判断 vector 是否为空 bool
reserve() 对 vector 预留空间 void

在这里插入图片描述

2.3.1 size()

size 的主要作用是返回当前 vector 中有效元素的个数,由于有效元素个数不可能是负值,所以返回值类型是 size_t 类型

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	cout << v1.size() << endl;
	return 0;
}

在这里插入图片描述

2.3.2 capacity()

capacity 的主要作用是返回当前容器的总容量, 总容量大于等于有效元素个数 size(),由于容量不可能是负值,所以返回值类型是 size_t 类型

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	cout << v1.capacity() << endl;
	return 0;
}

在这里插入图片描述

2.3.4 resize()

在这里插入图片描述
resize 的主要作用是对有效元素的个数进行缩减或增加,它的规则为:

(1)n > 总容量

先对 vector 进行扩容,扩充的容量可能等于 n,也可能大于 n,然后再增加有效元素至 n 个,用 val 来进行填充,val 具有缺省值,若 vector 存 int 类型的数据,那么它就是 0

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	v1.resize(10);
	cout << "capacity: " << v1.capacity() << endl;
	cout << "size: " << v1.size() << endl;
	return 0;
}

增加元素之前:

在这里插入图片描述

增加元素之后:

在这里插入图片描述

(2)总容量 > n > 当前有效元素个数

不进行扩容,增加有效元素至 n 个,用 val 来进行填充

增加元素前:

在这里插入图片描述

增加元素后:

在这里插入图片描述

(3)n < 当前有效元素个数

减少有效元素至 n 个

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	cout << "capacity: " << v1.capacity() << endl;
	cout << "size: " << v1.size() << endl;
	v1.resize(4);
	cout << "capacity: " << v1.capacity() << endl;
	cout << "size: " << v1.size() << endl;
	return 0;
}

减少元素前:

在这里插入图片描述

减少元素后:

在这里插入图片描述

2.3.5 empty()

empty 的作用是判断 vector 是否为空,是空返回 true,不是空则返回 false

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	vector<int> v2;
	if (v1.empty())
		cout << "v1 is empty" << endl;
	else
		cout << "v1 is not empty" << endl;

	if (v2.empty())
		cout << "v2 is empty" << endl;
	else
		cout << "v2 is not empty" << endl;
	return 0;
}

在这里插入图片描述

2.3.6 reserve()

reserve 的作用是为 vector 预留空间,预留空间时遵守以下的规则:

(1)n > 当前容量

进行扩容,扩充后的容量可能大于 n ,也可能等于 n

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	cout << "capacity: " << v1.capacity() << endl;
	v1.reserve(10);
	cout << "capacity: " << v1.capacity() << endl;
	v1.reserve(20);
	cout << "capacity: " << v1.capacity() << endl;
	v1.reserve(30);
	cout << "capacity: " << v1.capacity() << endl;
	v1.reserve(40);
	cout << "capacity: " << v1.capacity() << endl;
	return 0;
}

在这里插入图片描述

(2)n <= 当前容量

不对 vector 的容量进行任何操作

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	cout << "capacity: " << v1.capacity() << endl;
	v1.reserve(6); //等于
	cout << "capacity: " << v1.capacity() << endl;
	v1.reserve(4); //小于
	cout << "capacity: " << v1.capacity() << endl;
	return 0;
}

在这里插入图片描述

2.4 vector 的增删改查

vector 的增删改查接口主要有以下几种:

接口名称 作用 返回值类型
operator[] () 根据下标返回 vector 中元素 引用
front() 返回 vector 的第一个元素 引用
back() 返回 vector 的最后一个元素 引用
push_back() 在 vector 中尾插一个元素 void
pop_back() 在 vector 中进行尾删 void
insert() 在 vector 中插入元素 Iterator/void
erase() 在 vector 中删除元素 Iterator
swap() 交换 vector 中的所有元素 void
clear() 清除 vector 中的所有元素 void

在这里插入图片描述

2.4.1 operator[]

在这里插入图片描述
operator[] 的作用主要是通过下标 n 来获取 vector 中的元素,返回它的引用,返回引用的目的是为了外界能够像使用数组的下标访问一样修改 vector 中的元素如果对象是 const 对象,那么会返回 const 引用,就意味着不能更改该对象的值

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	cout << "v1[0]:" << v1[0] << endl;
	cout << "v1[0]:" << v1.operator[](0) << endl;

	const vector<int> v2 = { 1,2,3,4,5,6 };
	cout << "v2[0]:" << v2[0] << endl;
	cout << "v2[0]:" << v2.operator[](0) << endl;
	return 0;
}

在这里插入图片描述

2.4.2 front()

在这里插入图片描述
front 的作用是访问 vector 的第一个元素,等同于 operator[] (0),返回的也是该元素的引用对于 const 对象,返回的就是 const 引用,就意味着不能更改该对象的值

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	cout << "v1 front:" << v1.front() << endl;
	return 0;
}

在这里插入图片描述

2.4.3 back()

在这里插入图片描述
back 的作用是访问 vector 的最后一个元素,等同于 operator[] (size() - 1),返回的也是该元素的引用对于 const 对象,返回的就是 const 引用,就意味着不能更改该对象的值

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	cout << "v1 back:" << v1.back() << endl;
	return 0;
}

在这里插入图片描述

2.4.4 push_back()

push_back 的作用是在 vector 中尾插一个元素 val 并更新 size()

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	v1.push_back(7);
	return 0;
}

在这里插入图片描述

2.4.5 pop_back()

pop_back 的作用是删除 vector 中的最后一个元素并更新 size()

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	v1.pop_back();
	return 0;
}

在这里插入图片描述

2.4.6 insert()

在这里插入图片描述
insert 的作用是在 vector 中的指定位置插入值并更新 size()插入值时,如果是在中间或者最前方,则需要将后面的元素向后进行移动,时间复杂度较高,插入的方式有三种:

(1)在指定位置插入一个值

此时在插入完成后,会 返回指向新元素的迭代器

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	v1.insert(v1.begin() + 1, 8);
	return 0;
}

在这里插入图片描述

(2)在指定的位置插入 n 个同样的值

此时在插入完成后,不会返回任何值

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	v1.insert(v1.begin() + 1, 5, 7);
	return 0;
}

在这里插入图片描述

(3)在指定位置插入一个区间内的值

这里的区间范围需要给出 起始迭代器和终止迭代器 来确定,并且区间是左闭右开的,插入完成后,不会返回任何值

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	vector<int> v2;
	v2.insert(v2.begin(), v1.begin(), v1.end()); // [begin(), end())
	return 0;
}

在这里插入图片描述

注意事项:
在 insert 插入元素后,如果要通过迭代器来修改值,一定要接受返回值,不然会引发迭代器失效的问题,因为 insert 插入元素时,会将后面的元素整体向后移动,移动完成后,后方元素的迭代器和原来就不同了 ,比如:

在这里插入图片描述

此时,假设原来有个旧迭代器指向 3,但是经过了元素的移动后,现在它指向了 2,使用它进行操作是不合适的

在 insert 插入元素时,可能原来的空间已经满了,这个时候就需要开辟新空间,将原空间的元素复制到新空间中并释放旧空间,再进行插入,如果这个时候使用旧的迭代器,就会访问已经失效的旧空间,相当于野指针访问,是十分危险的行为,比如:

在这里插入图片描述

2.4.7 erase()

在这里插入图片描述
erase 的作用是删除 vector 中指定位置的元素并更新 size(),返回下一个元素的迭代器删除的方式有两种:

(1)删除指定位置上的值

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	v1.erase(v1.begin());
	return 0;
}

在这里插入图片描述

(2)删除指定区间内的值

在使用区间删除时,区间是左闭右开的,区间也需要起始迭代器和终止迭代器来确定

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	v1.erase(v1.begin() + 1, v1.end() - 1); //[begin() + 1, end() - 1)
	return 0;
}

在这里插入图片描述

在这里插入图片描述
注意事项:

在使用 erase 删除数据后一定要接受返回值,不然会导致迭代器失效的问题,因为删除一个元素会导致后面元素的前移,后面元素的迭代器就发生了变化,旧的迭代器无法访问到原来的值,迭代器失效,还可能引出额外的问题,比如:

在末尾是偶数时,删除 vector 里面的所有偶数可能引出越界问题

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	vector<int>::iterator it = v1.begin();
	while (it != v1.end())
	{
		if (*it % 2 == 0)
			v1.erase(it);
		it++;
	}
	return 0;
}

在这里插入图片描述
在不断删除的过程中,由于对迭代器的错误使用,导致迭代器与 vector 的末尾相互错过,引发了越界问题,正确的写法应该是:

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	vector<int>::iterator it = v1.begin();
	while (it != v1.end())
	{
		if (*it % 2 == 0)
			it = v1.erase(it);
		else
			it++;
	}
	return 0;
}

在有连续的偶数时,删除 vector 里面的所有偶数可能会导致数据的不干净删除

int main()
{
	vector<int> v1 = { 1,2,4,4,4,6 };
	vector<int>::iterator it = v1.begin();
	while (it != v1.end())
	{
		if (*it % 2 == 0)
			v1.erase(it);
		it++;
	}
	return 0;
}

在这里插入图片描述
在不断删除的过程中,后面的元素不断前移,但是迭代器也在不断后移,就可能发生明明迭代器指向的是偶数却没有删除的情况,正确写法为:

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	vector<int>::iterator it = v1.begin();
	while (it != v1.end())
	{
		if (*it % 2 == 0)
			it = v1.erase(it);
		else
			it++;
	}
	return 0;
}

2.4.8 swap()

swap 的作用是交换两个 vector 的值

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	vector<int> v2 = { 7,8,9,10 };
	v1.swap(v2);
	return 0;
}

在这里插入图片描述

2.4.9 clear()

clear 的作用是清除一个 vector 的值, 在清除时,不会释放 vector 的空间,只是清除了其中的元素

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	v1.clear();
	return 0;
}

在这里插入图片描述

3 vector 的总结

vector
底层数据结构 动态顺序表,可不断进行扩容
查找/修改效率 由于支持用下标随机访问,所以查找/修改效率高
插入/删除效率 在尾插尾删时,由于后方没有其他元素了,所以不用移动元素,效率高。在头插/头删,中间插入删除时,需要大量移动元素,时间复杂度为O(N),效率低
空间利用率 由于是顺序表,空间是连续的,不会产生内存碎片,并且在处理时,会一次读入一片连续的数据,空间利用率高
迭代器类型 属于随机迭代器,支持++,- -,-,+ 的操作
迭代器失效 在插入删除时,都会出现,插入会导致后面的元素迭代器失效,删除也是一样的
应用场景 对插入删除要求少,需要多次进行访问/修改时

更多推荐