【STL】——顺序容器

本文详细介绍了STL中的三种顺序容器:vector、list和deque。对比了它们的基本操作、扩容机制、特点以及在插入、删除、查找等操作上的时间复杂度。vector适合尾部快速插入删除,支持随机访问;list插入删除高效但访问慢;deque则支持双端快速插入删除,内存利用率较低。
摘要由CSDN通过智能技术生成


引言
c++ STL又叫 standard template libaray 标准模板库。容器又是我们STL里面非常重要的模块。
下面列出来的是我们常用到的C++STL容器
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这篇文章我们就主要来讲解一下顺序容器里面的vector、list还有deque吧~

1、引言

首先,为了更加清晰明了的了解到每个顺序容器,首先,我将他们的共同点都提取出来。
一、构造函数
系统为顺序容器提供了4种构造方式,即定义方式,以vector为例:

  1. 默认构造: 直接开辟空间;如vector vec
  2. 带有一个整型n的构造:开辟n个大小的空间,用0初始化;如vector<int> vec(10),开辟10个空间,初始化为0
  3. 带有2个参数n,m的构造函数 :开辟n个空间,用m初始化。如vector<int>vec(10,20),开辟10个空间,用20初始化。
  4. 传入迭代器(指针)区间的构造函数:传入起始位置,末尾位置,会把起始~末尾的迭代器区间元素传入容器。注意也可以传入数组,字符串的指针。如vector<int> vec(arr,arr+len),将arr~arr+len区间的数据传入容器。

list,deque同理,都有这四种构造方式。

二、函数

格式 含义
c.begin(); 返回一个迭代器,它指向容器c的第一个元素
c.end() 返回一个迭代器,它指向容器c的最后一个元素的下一位置,是一个无效位置
c.rbegin() 返回一个逆迭代器,它指向容器c的最后的一个元素
c.rend() 返回一个逆迭代器,它指向容器c的第一个元素前面的位置,是一个无效位置
c.size() 返回容器c中的元素个数
c.max_size() 返回容器c可容纳的最多元素个数
c.empty() 返回容器大小是否为0的布尔值
c.clear() 删除容器c内的所有元素。返回void

三、遍历方式
可以通过迭代器来遍历容器中的元素。
以vector为例,list,deque同理:

vector<int> it = vec.begin();
for (; it != vec.end(); ++it)
{
   
	cout << *it << " ";
}

2、vector(数组)

vector容器几种构造方式
首先,我们看看系统提供了几种方式,具体的使用如下:

int main()
{
	std::vector<int> vec1;
	std::vector<int> vec2(10);
	std::vector<int> vec3(10,20);

	int arr[] = { 1,2,3,4,5,6,7 };
	int len = sizeof(arr) / sizeof(arr[0]);
	std::vector<int> vec4(arr, arr + len);
	return 0;
}

根据上面的实践,对于vector的构造函数可以总结如下:

  1. 默认构造:就开辟一个空间就可以
  2. 带有一个参数的构造:参数代表n个大小的空间,每个空间用0来初始化
  3. 带两个参数的构造:需要n个个大小的空间,每个空间都存放元素val
  4. 数组类型的构造函数:传入数组的区间。

【补充】这个区间也可以用通过迭代器的方式传入。具体实现遍历打印如下:

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

2.1基本操作

1、增

  • 尾插 push_back:没有数据移动O(1)
    【举个栗子】
std::vector<int> vec;
   for (int i = 0; i < 5; i++)
   {
   	vec1.push_back(i + 1);
   }
  • 按位置插入 insert:通过迭代器代表插入位置,因为需要移动数据所以时间复杂度是 ==O(n) ==
    【举个栗子】
std::vector<int>::iterator it = vec.begin() + 2;
   vec.insert(it, 100);

以下表示insert的四种常用用法

插入格式 形式 含义 时间复杂度
尾插 push_back(a) 将数据a尾插到容器中 尾插没有数据移动,所以时间复杂度为O(1)
任意位置插入 insert(index,first,last) 将first~last区间内的元素插入index位置 存在数据的移动,时间复杂度为O(n)
任意位置插入 insert(index,val) 将val元素插入index位置 存在数据的移动,时间复杂度为O(n)
任意位置插入 insert(index,count,val) 在index位置插入count个val元素 存在数据的移动,时间复杂度为O(n)

2、删

  • 尾删pop_back:末尾删除元素O(1)
  • 按位置删除erase:删除迭代器指向的元素== O(n)==
    没有头删,只有尾删和任意位置删除,
删除格式 形式 含义 时间复杂度
尾删 pop_back() 将容器最后一个元素删除 尾删没有数据移动,所以时间复杂度为O(1)
任意位置删除 erase(first,last) 删除first~last区间的元素 存在数据的移动,时间复杂度为O(n)
任意位置删除 erase(it) 删除it位置的元素 存在数据的移动,时间复杂度为O(n)

【举个栗子】

vec.erase(vec.begin(), vec.begin() + 2);

3、查

因为底层是一个数组,数组最大的特点就是通过数组下标随机访问,所以时间复杂度是== O(1)==。通过迭代器对vector容器遍历主要有以下两种方式。

方式一

	auto it1 = vec.begin();
	for (; it1 != vec.end(); ++it1)
	{
		cout << *it1 << " ";
	}

方式二

	int size = vec.size();
	for (int i = 0; i < size; i++)
	{
		cout << vec[i] << " ";
	}

4、其他操作

  • size():返回容器底部有效的数据元素
  • empty():判断容器是否为空
  • reserve(20):给vector预留空间 只给容器底层开辟指定大小的内存空间,并不会添加新的元素
  • resize(20):容器扩容 不仅给容器底层开辟指定大小的内存空间,还会添加新的元素
  • swap:两个容器进行元素交换

2.2扩容机制

1、reserve和resize的区别
1、reserve主要是给vector容器预留空间,如下代码验证:

int main()
{
   
	vector<int> vec;//默认开辟的vector,底层空间为0
	vec.reserve(20);

	cout << vec.empty() << endl;//输出布尔值
	cout << vec.size() << endl;
	
	for (int i = 0; i < 20; i++)
	{
   
		vec.push_back(rand() % 100 + 1);
	}
	cout << vec.empty() << endl;
	cout << vec.size() << endl;
	return 0;
}

运行结果如下:
在这里插入图片描述
从上面的运行结果可以看出,reserve给vector容器预留空间20但是里面并没有数据所以empty为true。==直到有数据的添加,empty才为false。但是添加的过程并不会导致容量的增加。所以最后还是20的容量。

2、resize就是容器扩容,如下代码验证

int main()
{
   
	vector<int> vec;//默认开辟的vector,底层空间为0
	vec.resize(20);

	cout << vec.empty() << endl;//输出布尔值
	cout << vec.size() << endl;
	
	for (int i = 0; i < 20; i++)
	{
   
		vec.push_back(rand() % 100 + 1);
	}
	cout << vec.empty() << endl;
	cout << vec.size() << endl;
	return 0;
}

运行结果如下:
在这里插入图片描述
从运行结果可以看到,不仅仅开辟了20个空间还在里面存放了20个为0的整形元素,再添加20个元素的时候开始扩容所以empty为false.添加的过程又增加了20个元素。所以最后输出40.
2、扩容具体流程
由此,可以总结vector容器的扩容机制如下:

  1. 开辟一定大小,
  2. 以倍数的形式开辟更大的空间,通常以2倍开辟
  3. 把旧的数据拷贝到新空间中
  4. 释放旧空间
  5. 指针指向新空间,调整总大小。

2.3对容器进行连续插入或者删除操作时迭代器失效问题

对容器进行连续插入或者删除操作(insert/erase),一定要更新迭代器,否则第一次insert或者erase完成,迭代器就失效了

1、连续删除的情况
【举个栗子】实现把vec容器中所有的偶数全部删除
代码实现如下:

int main()
{
   
vector<int> vec;
auto it2 = vec.begin();
	while (it2 != vec.end())
	{
   
		if (*it2 % 2 == 0)
		{
   

			it2 = vec.erase(it2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值