142

问答题 142 /413

请你说一说STL常用的容器

参考答案

参考回答:

(1)vector

vector是一种动态数组,在内存中具有连续的存储空间,支持快速随机访问。由于具有连续的存储空间,所以在插入和删除操作方面,效率比较慢。vector有多个构造函数,默认的构造函数是构造一个初始长度为0的内存空间,且分配的内存空间是以2的倍数动态增长的,即内存空间增长是按照20,21,22,23.....增长的,在push_back的过程中,若发现分配的内存空间不足,则重新分配一段连续的内存空间,其大小是现在连续空间的2倍,再将原先空间中的元素复制到新的空间中,性能消耗比较大,尤其是当元素是非内部数据时(非内部数据往往构造及拷贝构造函数相当复杂)。

API:

vector <T> v;//采用模板实现类实现,默认构造函数

assign(begin(),end());//将【begin(),end()】区间中的元素拷贝给本身

size();//返回元素容器中元素个数

at(int idx);//返回索引idx所指的数据,如果idx越界,抛出out_of_range异常

(2)deque

deque和vector类似,支持快速随机访问。二者最大的区别在于,vector只能在末端插入数据,而deque支持双端插入数据。deque的内存空间分布是小片的连续,小片间用链表相连,实际上内部有一个map的指针。deque空间的重新分配要比vector快,重新分配空间后,原有的元素是不需要拷贝的。

API:

deque<T>deqT;//默认构造形式

assign(begin,end);//将【begin,end】区间的数据拷贝赋值给自身

deque.size();//返回容器中元素的个数

push_back(elem);//在容器尾部添加一个数据

(3)list

list是一个双向链表,因此它的内存空间是可以不连续的,通过指针来进行数据的访问,这使list的随机存储变得非常低效,因此list没有提供[]操作符的重载。但list可以很好地支持任意地方的插入和删除,只需移动相应的指针即可。

API:

list<T>lstT;//list采用模板类实现对象的默认构造函数

push_back(elem);//在容器尾部加入一个元素

size();//返回元素容器中元素个数

empty();//判断容器是否为空

(4)map

map是一种关联容器,该容器用唯一的关键字来映射相应的值,即具有key-value功能。map内部自建一棵红黑树(一种自平衡二叉树),这棵树具有数据自动排序的功能,所以在map内部所有的数据都是有序的,以二叉树的形式进行组织。

API:

map<t1,T2>mapT;//map默认构造函数

size();//返回元素中元素的数目

empty();//判断容器是否为空

clear();//删除所有元素

(5)set

set也是一种关联性容器,它同map一样,底层使用红黑树实现,插入删除操作时仅仅移动指针即可,不涉及内存的移动和拷贝,所以效率比较高。set中的元素都是唯一的,而且默认情况下会对元素进行升序排列。所以在set中,不能直接改变元素值,因为那样会打乱原本正确的顺序,要改变元素值必须先删除旧元素,再插入新元素。不提供直接存取元素的任何操作函数,只能通过迭代器进行间接存取。

API:

set<T>sT;//set默认构造函数

size();//返回容器中元素的数目

empty();//判断容器是否为空

insert(elem);//在容器中插入元素

clear();//清除所有元素

(6)queue

queue是一个队列,实现先进先出功能,queue不是标准的STL容器,却以标准的STL容器为基础。queue是在deque的基础上封装的。之所以选择deque而不选择vector是因为deque在删除元素的时候释放空间,同时在重新申请空间的时候无需拷贝所有元素。

API:

queue<T>queT;//queue采用模板类实现,queue对象的默认构造形式

push(elem);//往队尾添加元素

(7)stack

stack是实现先进后出的功能,和queue一样,也是内部封装了deque,这也是为啥称为容器适配器的原因吧(纯属猜测)。自己不直接维护被控序列的模板类,而是它存储的容器对象来为它实现所有的功能。stack的源代码原理和实现方式均跟queue相同。

API:

stack<T>stkT;//采用模板类实现,stack对象的默认构造形式

push(elem);//向栈顶添加元素