【c++】——STL容器之vector,queue,list

一、STL分类

c++ STL又叫 standard template libaray 标准模板库。
下面列出来的是我们常用到的C++STL容器
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
容器中,对象的构造析构,内存的开启释放是通过容器的空间配置器allocator实现的
allocate 负责内存开辟
dealocate 负责内存释放
construct 对象构造
destory 对象析构

二、vector

1、底层数据结构
动态开辟的数组,每次以原来空间大小的2倍进行扩容vector vec;
2、相关接口
在这里插入图片描述
增加:
vec.push_back(20);在容器末尾添加元素,O(1) 导致容器扩容
vec.insert(it,20);在迭代器指向的位置添加一个元素20 O(n) 导致容器扩容

删除:
vec.pop_back();末尾删除元素O(1)
vec.erase(it);删除迭代器指向的元素 O(n)
注意:对容器进行连续插入或者删除操作(insert/erase),一定要更新迭代器,否则第一次insert或者erase完成,迭代器就失效了

例子一:实现把vec容器中所有的偶数全部删除

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

			it2 = vec.erase(it2);
		}
		else
		{
			++it2;
		}
	}
	return 0;
}

如果只是vec.erase(it2);执行完过后迭代器就失效了,it2不能再加加了。所以要对迭代器进行更新一次,删除以后,后面的元素挪到了当前迭代器的位置,所以不用it2++
另外,当前元素继续判断,如果当前元素不是偶数,才要进行迭代器++
运行结果如下:
在这里插入图片描述
例子二:给vector容器中所有的奇数前面都添加一个小于奇数1的偶数(连续插入)

int main()
{
vector<int> vec;
auto it1 = vec.begin();
	for (it1 = vec.begin(); it1 != vec.end(); ++it1)
	{
		if (*it1 % 2 != 0)
		{
			it1=vec.insert(it1, *it1 - 1);//要对迭代器进行更新
			it1++;
		}
	}
	return 0;
}

要对迭代器进行更新.找到了奇数要进行两次++操作,如下图所示,找到了47在其之前插入46,++it1将46插入,it1还应该++对迭代器进行更新寻找下一个奇数。在这里插入图片描述

查询:
operator[];因为底层是一个数组,数组最大的特点就是通过数组下标随机访问vec[5] O(1)
iterator迭代器进行遍历
find ,for_each
foreach => 通过iterator来实现

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

在这儿,我们要特别区分一下reserve和resize。
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,添加的过程并不会导致容量的增加。所以最后还是20的容量。

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.
4、通过迭代器对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] << " ";
	}

三、queue和list

1、双端队列容器queue

1、底层数据结构

动态开辟的二维数组,一位数组从2开始,以2倍的方式进行扩容,每次扩容后,原来第二维的数组,从新的第一维数组的下标oldsize/2开始存放,上下都预留相同的空行,方便支持deque的首尾元素添加deque deq;
如下所示,当一个队列满了,则向下扩充一个队列
在这里插入图片描述
当两个队列都满了,再扩充
在这里插入图片描述
2、相关接口
在这里插入图片描述
增加:
deq.push_back(20);从末尾添加元素O(1)
deq.push_front(20);从首部添加元素O(1) //vec.insert(vec.begin(),20) O(n)
deq.insert(it,20);it指向的位置添加元素O(n)

删除:
deq.pop_back();从末尾删除元素 O(1)
deq.pop_front();从首部删除元素O(1)
deq.erase(it);从it指向的位置删除元素O(n)

查询搜索:
iterator(连续的insert和erase一定要考虑迭代器失效的问题)

2、链表容器list

1、底层数据结构
双向的循环链表 pre data next,如图所示:
在这里插入图片描述
2、相关接口
在这里插入图片描述
增加:
mylist.push_back(20);从末尾添加元素O(1)
mylist.push_front(20);从首部添加元素O(1) //vec.insert(vec.begin(),20) O(n)
mylist.insert(it,20);it指向的位置添加元素O(1) 链表中进行insert的时候,先要进行一个query查询操作
对于链表来说,查询操作效率就比较慢了

删除:
mylist.pop_back();从末尾删除元素 O(1)
mylist.pop_front();从首部删除元素O(1)
mylist.erase(it);从it指向的位置删除元素O(n)

查询搜索:
iterator(连续的insert和erase一定要考虑迭代器失效的问题)

deque和list 比vector容器多出来的增加删除函数接口:
push_front 和pop_fornt

四、vector、queue、list的区别

1、vector和deque之间的区别

vector特点:动态开辟的数组,内存是连续的,2倍的方式进行扩容 vector vec;

deque特点:动态开辟的二维数组空间,deque底层内存不是连续的第二维是固定长度的数组空间,扩容的时候(把第一维的数组进行二倍扩容)
1、底层数据结构的区别
2、前中后插入元素的时间复杂度
中间和末尾的时间复杂度相同O(1),前面插入deque是O(1)因为是双端队列在前或后都可以扩充,但是vectorO(n)

3、对于内存的使用效率
vector需要的内存空间必须是连续的,deque可以分块进行数据存储不需要内存空间必须是连续的

4、在中间进行insert或者erase,效率vector好一点,deque差一点。
虽然时间复杂度都是O(n),但是要考虑挪动的方便性由于deque的第二维内存空间不是连续的,所以在deque中间进行元素的insert或者erase,造成元素移动的时候比vector慢。
如下图所示:
在这里插入图片描述
在deque中,每一个第二维是连续的(重新new出来的),是一种分段连续。
删除了一个元素,后面元素的移动首先要找到一维去,存了起始位置的地址,然后再根据偏移量找到元素移动位置。
但是vector的删除就很简便了,如下图所示:
在这里插入图片描述
因为他的存储空间是连续的,所以依次挨个移动即可。

2、vector和list之间的区别

这两个的区别就类似于数组和链表的区别,这样一联想,就很容易得出答案了

1、 底层数据结构
一个是动态数组,一个是双向循环链表

2. 增删改查时间复杂度

数组 增加删除O(n) 查询O(n) 随机访问O(1)
链表考虑搜索的时间 其余删除查询的时间复杂度都是O(1)

发布了98 篇原创文章 · 获赞 9 · 访问量 3659

猜你喜欢

转载自blog.csdn.net/qq_43412060/article/details/105266945
今日推荐