C++面试题系列:STL

目录

使用过哪些容器,底层如何实现的?

STL 容器用过哪些,查找的时间复杂度是多少,为什么?

简述 vector 的实现原理

请你说说 deque 的实现原理

请你说说 set 的实现原理

 请你说说迭代器失效原因,有哪些情况

有没有使用过仿函数?

介绍一下STL的allocaotr

面试题(60)|STL(6):map常见面试题和用法总结

STL里resize和reserve的区别

面试题(55)|STL(5):vector删除指定值的元素

请你说说 vector 的扩容机制,扩容以后,它的内存地址会变化吗?

 说一说 vector 和 list 的区别,分别适用于什么场景?


更多见:C++面试题系列

使用过哪些容器,底层如何实现的?

 ** 标准回答**
  STL 中容器分为顺序容器、关联式容器、无序关联式容器、容器适配器三种类型,三种类型容器特性分别如下:
 
   1. 顺序容器 又叫序列式容器,容器并非排序的,元素的插入位置同元素的值无关,包含 vector、deque、list、array、forward_list 。
  - vector:动态数组 元素在内存连续存放。随机存取任何元素都能在常数时间完成。在尾端增删元素具有较佳的性能。
   - deque:双向队列 元素在内存连续存放。随机存取任何元素都能在常数时间完成(仅次于 vector )。在两端增删元素具有较佳的性能(大部分情况下是常数时间)。 
  - list:双向链表 元素在内存不连续存放。在任何位置增删元素都能在常数时间完成。不支持随机存取。
  
   2. 关联式容器 元素是排序的;插入任何元素,都按相应的排序规则来确定其位置;在查找时具有非常好的性能;通常以平衡二叉树的方式实现,包含set、multiset、map、multimap。 
   - set/multiset set中不允许相同元素,multiset 中允许存在相同元素。 
   - map/multimap map 与 set 的不同在于 map 中存放的元素有且仅有两个成员变,一个名为 first,另一个名为 second,map 根据 first 值对元素从小到大排序,并可快速地根据 first 来检索元素。map 和multimap 的不同在于是否允许相同 first 值的元素。

3. 无序关联式容器 unordered_map、unordered_multimap、unordered_set、unordered_multiset 
   
4. 容器适配器 封装了一些基本的容器,使之具备了新的函数功能,包含 stack、queue、priority_queue。 
 - stack:栈 栈是项的有限序列,并满足序列中被删除、检索和修改的项只能是最进插入序列的项(栈顶的项),后进先出。
  - queue:队列 插入只可以在尾部进行,删除、检索和修改只允许从头部进行,先进先出。 
  - priority_queue:优先级队列 内部维持某种有序,然后确保优先级最高的元素总是位于头部,最高优先级元素总是第一个出列。

STL 容器用过哪些,查找的时间复杂度是多少,为什么?

标准回答
 STL 中常用的容器有 vector、deque、list、map、set、multimap、multiset、unordered_map、unordered_set 等。
 容器底层实现方式及时间复杂度分别如下:
  1. vector 采用一维数组实现,元素在内存连续存放,不同操作的时间复杂度为: 插入: O(N) 查看: O(1) 删除: O(N) 
  2.  deque 采用双向队列实现,元素在内存连续存放,不同操作的时间复杂度为: 插入: O(N) 查看: O(1) 删除: O(N) 
  3.  list 采用双向链表实现,元素存放在堆中,不同操作的时间复杂度为: 插入: O(1) 查看: O(N) 删除: O(1) 
  4.  map、set、multimap、multiset 上述四种容器采用红黑树实现,红黑树是平衡二叉树的一种。不同操作的时间复杂度近似为: 插入: O(logN) 查看: O(logN) 删除: O(logN) 
  5. unordered_map、unordered_set、unordered_multimap、 unordered_multiset 上述四种容器采用哈希表实现,不同操作的时间复杂度为: 插入: O(1),最坏情况O(N) 查看: O(1),最坏情况O(N) 删除: O(1),最坏情况O(N) 

注意:容器的时间复杂度取决于其底层实现方式。

简述 vector 的实现原理

得分点 动态数组、连续存储空间、扩容

标准回答

vector 是一种动态数组,在内存中具有连续的存储空间,支持快速随机访问,由于具有连续的存储空间,所以在插入和删除操作方面,效率比较慢。

当 vector 的大小和容量相等(size==capacity)也就是满载时,如果再向其添加元素,那么 vector 就需要扩容。

vector 容器扩容的过程需要经历以下 3 步:

1. 完全弃用现有的内存空间,重新申请更大的内存空间;

2. 将旧内存空间中的数据,按原有顺序移动到新的内存空间中;

3. 最后将旧的内存空间释放。

vector 扩容是非常耗时的。为了降低再次分配内存空间时的成本,每次扩容时 vector 都会申请比用户需求量更多的内存空间(这也就是 vector 容量的由来,即 capacity>=size),以便后期使用。

请你说说 deque 的实现原理

得分点
分段连续内存、中控器

标准回答
deque 是由一段一段的定量的连续空间构成。

一旦有必要在 deque 前端或者尾端增加新的空间,便配置一段连续定量的空间,串接在 deque 的头端或者尾端。

deque 最大的工作就是维护这些分段连续的内存空间的整体性的假象,并提供随机存取的接口,避开了重新配置空间,复制,释放的轮回,代价就是复杂的迭代器架构。

既然 deque 是分段连续内存空间,那么就必须有中央控制,维持整体连续的假象,数据结构的设计及迭代器的前进后退操作颇为繁琐。

deque 采取一块所谓的 map(不是 STL 的 map 容器)作为主控,这里所谓的 map 是一小块连续的内存空间,其中每一个元素(此处成为一个结点)都是一个指针,指向另一段连续性内存空间,称作缓冲区。缓冲区才是 deque的存储空间的主体。

请你说说 set 的实现原理

得分点 红黑树、排序

标准回答

set 底层使用红黑树实现,一种高效的平衡检索二叉树。

set 容器中每一个元素就是二叉树的每一个节点,

对于 set 容器的插入删除操作,效率都比较高,原因是二叉树的删除插入元素并不需要进行内存拷贝和内存移动,只是改变了指针的指向。

对 set 进行插入删除操作 都不会引起迭代器的失效,因为迭代器相当于一个指针指向每一个二叉树的节点,对 set的插入删除并不会改变原有内存中节点的改变。

set 中的元素都是唯一的,而且默认情况下会对元素进行升序排列。

不能直接改变元素值,因为那样会打乱原本正确的顺序,要改变元素值必须先删除旧元素,再插入新元素。

不提供直接存取元素的任何操作函数,只能通过迭代器进行间接存取。

 请你说说迭代器失效原因,有哪些情况

标准回答

STL 中某些容器调用了某些成员方法后会导致迭代器失效。

例如 vector 容器,如果调用 reserve() 来增加容器容量,之前创建好的任何迭代器(例如开始迭代器和结束迭代器)都可能会失效,这是因为,为了增加容器的容量,vector 容器的元素可能已经被复制或移到了新的内存地址。

1. 序列式容器迭代器失效

对于序列式容器,例如 vector、deque,由于序列式容器是组合式容器,当当前元素的迭代器被删除后,其后的所有元素的迭代器都会失效,这是因为 vector、deque都是连续存储的一段空间,所以当对其进行 erase 操作时,其后的每一个元素都会向前移一个位置。

解决:erase 返回下一个有效的迭代器。

2. 关联式容器迭代器失效

对于关联容器,例如如 map、 set,删除当前的迭代器,仅仅会使当前的迭代器失效,只要在 erase 时,递增当前迭代器即可。这是因为 map 之类的容器,使用了红黑树来实现,插入、删除一个节点不会对其他点造成影响。erase 迭代器只是被删元素的迭代器失效,但是返回值为 void,所以要采用 erase(iter++) 自增方式删除迭代器。

有没有使用过仿函数?

介绍一下STL的allocaotr

面试题(60)|STL(6):map常见面试题和用法总结

STL里resize和reserve的区别

面试题(55)|STL(5):vector删除指定值的元素

请你说说 vector 的扩容机制,扩容以后,它的内存地址会变化吗?

得分点 申请空间、拷贝数据、释放旧空间

标准回答

当 vector 的大小和容量相等(size==capacity)也就是满载时,如果再向其添加元素,那么 vector 就需要扩容。

vector 容器扩容的过程需要经历以下 3 步:

1. 完全弃用现有的内存空间,重新申请更大的内存空间;

2. 将旧内存空间中的数据,按原有顺序移动到新的内存空间中;

3. 最后将旧的内存空间释放。

因为 vector 扩容需要申请新的空间,所以扩容以后它的内存地址会发生改变。vector 扩容是非常耗时的,为了降低再次分配内存空间时的成本,每次扩容时 vector 都会申请比用户需求量更多的内存空间(这也就是 vector 容量的由来,即 capacity>=size),以便后期使用。 

 说一说 vector 和 list 的区别,分别适用于什么场景?

得分点 低层数据结构、内存顺序、是否支持随机访问

标准回答

1. 区别

- vector 底层实现是数组,list 是双向链表 - vector 支持随机访问,list 不支持

- vector 是顺序内存,list 不是

- vector 在中间节点进行插入删除会导致内存拷贝,list 不会

- vector 一次性分配好内存,不够时才进行扩容,list 每次插入新节点都会进行内存申请

- vector 随机访问性能好,插入删除性能差,list 随机访问性能差,插入删除性能好

2. 适用场景

- vector拥有一段连续的内存空间,因此支持随机访问,如果需要高效的随即访问,而不在乎插入和删除的效率,使用 vector。

- list 拥有一段不连续的内存空间,如果需要高效的插入和删除,而不关心随机访问,则应使用list。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

haimianjie2012

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值