C++面试高频(六)之<STL>

1. vector list异同⭐⭐

List:

  • List是一个双向链表的容器,每个元素都由一个节点表示,节点包含指向前一个节点和后一个节点的指针。
  • 插入和删除元素的时间复杂度为O(1),因为只需要改变节点的指针,而不需要移动其他元素。
  • 由于节点间的指针关联,内存空间不是连续分配的,因此对于需要频繁的插入和删除操作的场景,list可以更高效。
  • 在访问元素方面,由于不具备随机访问特性,只能通过迭代器逐个遍历,时间复杂度为O(n)。
  • list可以有效地处理大规模数据的插入和删除,但相对于vector来说占用更多的内存空间。

Vector:

  • Vector是一个动态数组的容器,元素的存储是连续的。
  • 插入和删除元素的时间复杂度为O(n),因为可能需要移动其他元素,尤其是在中间位置插入或删除元素时。
  • 在访问元素方面,由于具备随机访问特性,可以通过下标访问元素,时间复杂度为O(1)。
  • 向量的扩容和收缩可能会导致内存的重新分配和元素的复制,因此在需要频繁插入和删除的场景下效率较低。
  • 向量的内存空间是连续分配的,因此对于需要高效的随机访问操作的场景,vector更合适。
  • 相对于list来说,vector在占用内存空间方面更为节省。

总结:

综上所述,list适用于需要频繁插入和删除元素的场景,对内存占用要求较高;vector适用于需要高效的随机访问和尾部插入操作的场景,对内存占用要求较低。选择合适的容器类型取决于具体的需求和对性能的要求。

2 .vector的底层实现⭐⭐

1、底层实现

Vector在堆中分配了一段连续的内存空间来存放元素。

包括三个迭代器,first 指向的是vector中对象的起始字节位置;last指向当前最后一个元素的末尾字节;end指向整个vector容器所占用内存空间的末尾字节。

  • vector是动态空间,随着元素的加入,它的内部机制会自动扩充空间以容纳新的元素。vector的关键技术在于对大小的控制以及重新分配时的数据移动效率。
  • vector采用的数据结构是线性的连续空间(泛型的动态类型顺序表),他以两个迭代器start和finish分别指向配置得来的连续空间中目前已将被使用的空间。迭代器end_of_storage指向整个连续的尾部。
  • vector在增加元素时,如果超过自身最大的容量,vector则将自身的容量扩充为原来的两倍。扩充空间需要经过的步骤:重新配置空间,元素移动,释放旧的内存空间。一旦vector空间重新配置,则指向原来vector的所有迭代器都失效了,因为vector的地址改变了。

3 .vector和deque的区别⭐⭐⭐

Deque(双端队列)的特点可以总结如下:

  1. 双端操作:可以在头部和尾部进行高效的插入和删除操作。
  2. 分段结构:内部实现采用分段的数据结构,只需要移动对应段内的元素,而不需要移动整个容器中的元素。
  3. 随机访问:支持通过索引进行随机访问,可以在常量时间内访问任意位置的元素。
  4. 迭代器稳定性:插入和删除不会使已存在的迭代器失效。
  5. 增长特性:根据需要自动增长内部存储空间。
  6. 内存分配效率:在增加新的段时分配更多的内存空间,提高内存利用效率。

Deque和vector的区别

Deque(双端队列)和Vector之间的区别可以概括如下:⭐⭐⭐

  1. 插入和删除操作:Deque支持在头部和尾部高效地插入和删除元素,而Vector只能在尾部进行高效操作。
  2. 内部实现:Deque采用分段的数据结构,而Vector是一块连续的存储空间。
  3. 内存分配方式:Deque在扩容时能更高效地利用内存,并避免频繁的重分配,而Vector需要重新分配整块连续的内存空间。
  4. 随机访问性能:Vector的随机访问性能更好,而Deque的随机访问相对较低。

Deque和vector和LIST使用场景:⭐⭐

  • 如果需要高效的随即存取,而不需要在乎插入和删除的效率,使用 vector;
  • 如果需要大量的插入和删除,而不需要关心随即存取,则应使用 list;
  • 如果需要随即存取,而且需要关心两端数据的插入和删除,则应使用 deque。

4.为什么ist里面还要再定义一个sort函数⭐

1.整个STL设计的指导思想是GP,即模板编程。在此思想的指导下,少了面向象编程的类继承、虚函数、多态等的设计,取而代之的是数据与方法的分离,表现在STL中将容器与算法分离,两者分别闭门造车,中间依靠迭代器联系。 2.故sort算法被单独剥离出来,与所有的容器分开。虽然想的挺好但是总有例外,list容器就是那个例外。从源码可以看出,sort函数用到的迭代器的操作链表是不可能做到的,故list需要设计自己的sort函数。

5 .STL底层数据结构实现⭐

Vector(动态数组)

底层数据结构为数组,支持快速随机访问和动态大小调整。

List(双向链表)

底层数据结构为链表,支持快速插入和删除

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

c++/嵌入式面经专栏 文章被收录于专栏

BG双9,目前在某外企。打算把之前校招时做的笔记通过专栏发出来,本专栏适合于C/C++、嵌入式方向就业的同学,本篇面经总结数千篇面经的知识集合,实时更新全网最新的嵌入式/C++最新内容,囊括了C语言、C++、操作系统、计算机网络、嵌入式、算法与数据结构、数据库等一系列知识点,在我看来这些是求职者在面试中必须掌握的知识点。最后呢祝各位能找到自己合适的工作。

全部评论
第8个,vector是连续的,它的插入的时间复杂度不应该是O(N)吗
1 回复 分享
发布于 2024-10-16 15:38 江苏
``` 第十点好乱,总结下: - 各司其职:这个问题涉及到操作系统怎么分配内存,当程序需要新的内存时,就需要os申请分配,分配内存是os的事情,编译器/cpp库只管申请就行了,没有权限干涉别人是具体怎么分配的 - 假空闲:操作系统的内存管理并不保证相邻的内存地址是空闲的。这是因为,堆上的内存可能已经被其他程序或者线程占用了,或者分配给了其他对象,即使看似“空闲”,操作系统也无法保证你可以继续使用。 - 以巨大的代价换一点方便的操作:判断后续内存区域是否空闲是一个昂贵的操作,可能涉及与操作系统的通信,这样会严重影响性能。每次 vector 扩展时检查是否有足够的空闲空间,虽然在某些情况下可以避免重新分配内存,但在大多数情况下是不可行的,因为判断内存可用性并不是编译器或标准库能够直接高效处理的任务。 - 跨平台抽象:C++ 标准库的内存分配逻辑与操作系统无关,不能直接依赖操作系统的特性来判断后续内存是否可用,确保跨平台兼容性。 ```
1 回复 分享
发布于 2024-10-13 17:26 广东
尽量使用vector, 它的实现复杂度较deque更低. 而在数据长度频繁改变的情况下, 使用deque更高效
1 回复 分享
发布于 2024-03-10 15:29 江西
第六点 :这里实际上就是强调erase的用法,erase会把迭代器的指向it删除,然后返回下一个迭代器的位置,这个新的位置需要迭代器重新接收一下,不然会出现未定义的错误。#include <iostream> #include <vector> int main() { std::vector<int> vec = {1, 2, 3, 4, 5}; for (auto it = vec.begin(); it != vec.end(); ) { if (*it == 3) { it = vec.erase(it); // 删除元素并返回下一个有效迭代器,这样写是对的 } else { ++it; } } for (int v : vec) { std::cout << v << " "; // 输出: 1 2 4 5 } return 0; } //------------------------------------------------------------------- for (auto it = vec.begin(); it != vec.end(); ++it) { // 错误写法 if (*it == 3) { vec.erase(it); // 删除元素,但未更新迭代器 } }</int></vector></iostream>
点赞 回复 分享
发布于 2024-10-13 17:27 广东
完全二叉树并没有父节点的值大于等于(或小于等于)其子节点的值这个特性吧?
点赞 回复 分享
发布于 2024-07-16 17:32 山东
您好,这个有PDF版的吗
点赞 回复 分享
发布于 2024-06-20 15:57 河南
打卡
点赞 回复 分享
发布于 2024-01-07 16:25 江苏

相关推荐

04-18 00:32
已编辑
中南大学 Java
点赞 评论 收藏
分享
评论
4
52
分享

创作者周榜

更多
牛客网
牛客企业服务