当前位置: 首页 > news >正文

CPP(容器)STL:

1、vector的底层实现?vector的扩容过程?push_back()会有什么操作?

vector的底层实现是一个动态数组。vector是一个连续内存空间的动态数组,能够根据下标随机访问,且可以在数组内进行插入删除操作。vector提供了动态扩容机制,因此称为动态数组。

底层实现是它使用三个指针来维护数组:start指向数组的起始位置finish指向数组中最后一个有效元素的后一个位置end指向整个连续内存空间的末尾。vector支持随机访问,其元素在内存中连续存储。当vector的元素数量超过当前容量时,它会重新分配更大的内存空间,通常是当前容量的两倍,并将现有元素复制到新的内存空间中。这种动态内存分配策略允许vector的大小动态变化,同时避免了频繁内存分配和释放的开销。

扩容机制:

  1. 触发条件:当插入元素(如 push_back)导致当前元素数量(size())等于容器容量(capacity())时,触发扩容 。
  2. 分配新内存:根据编译器的扩容策略(如 GCC 采用 2 倍扩容,MSVC 采用 1.5 倍扩容),申请一块更大的连续内存空间 。
  3. 数据迁移:将旧内存中的所有元素逐个拷贝或移动到新内存中(若元素支持移动语义,优先移动以减少开销) 。
  4. 释放旧内存:析构旧内存中的元素并释放原内存块。
  5. 更新指针:调整 vector 的内部指针(_start、_finish、_end_of_storage),指向新内存的起始、结束和容量边界。

push_back会有什么操作?

  1. 空间检查:首先检查vector.size()vector.capacity(),判断是否需要扩容;
  2. 扩充内存(如果需要):进行扩容和数据复制;
  3. 构造新元素push_back 会通过拷贝构造或移动构造来在 vector 的末尾构造新元素。
  4. 增加大小: 更新 vector 的大小(size),表示其中实际存储的元素数量。

2、push_back和emplace_back的区别?vector删除元素会不会释放空间?

  1. 构造方式不同
    • push_back: 接受一个已经构造好的对象的副本或移动对象,会调用拷贝构造函数或移动构造函数,将对象复制或移动到vector中 ;
    • emplace_back: 接受构造对象所需的参数, 然后调用对象的构造函数在容器内直接构造对象。对于复杂对象,emplace_back 通常比 push_back 更高效,因为它避免了额外的拷贝或移动操作
  1. 可变参数支持
    • emplace_back: 支持可变参数模板,可以传递任意数量和类型的参数,只要这些参数能匹配对象的构造函数。
    • push_back: 只能接受单个参数,即要添加的对象。

vector的内存是只增不减的,删除元素不会减少vector的占用内存,只有析构掉vector数组才会释放其空间。

3、vector和list的区别?

在 C++ 的标准模板库(STL)中,list 是一种双向链表容器。它属于序列容器的一种,允许在其任意位置高效地插入和删除元素,非连续内存空间,但不支持随机访问(即不能通过索引直接访问元素)。
区别:

  • vector是数组,占用连续内存,是顺序访问;list是双向链表,随机访问
  • vector是一次性分配好内存,不够时再扩容;list每插入新节点都会进行内存申请;
  • vector随机访问性能好、插入删除性能差;list随机访问性能差,插入删除性能好。

4、什么是deque?底层原理是什么?deque和vector的区别?

deque是一个双向开口的连续线性空间,可以在常数级的时间复杂度下对链表进行操作。

deque是以**分段连续空间组合成的,当需要增加新的空间时,只需要配置一段定量连续空间拼接在头部或者尾部即可。因此,deque需要维护整体的连续性**,这也是deque的迭代器比较复杂的原因。deque内部有一个指针指向map,map是一小块连续空间,其中每个元素称为节点(node),这个node是这个指针,指向一个缓冲区,也就是实际存放数据的地方。

deque是分段连续空间维持其"整体连续"假象的任务,落在了迭代器的operator++ 和 operator-- 两个运算符重载身上,当一个缓冲区到达边界时,则需要从一个缓冲区跳到下一个缓冲区,deque的插入删除等操作也需要结合缓冲区来实现。

deque除了维护一个指向map的指针外,也维护start、finish两个迭代器,分别指向第一缓冲区的第一个元素最后缓冲区的最后一个元素(的下一位置)

区别:

  • vector是一整块连续的内存来存储数据;deque是分段连续,在扩容时效率更高,但需要更复杂的迭代器来维护整体连续性;
  • vector只能在数组末尾实现常数级别的插入和删除,deque在头尾均可以。

5、set和map的区别?底层是怎么实现的?

  • set可以理解为只有key值(或者说key和value相等)的有序集合,它是按照key值排序的,并且不允许有重复的key值,key不允许修改,因此其迭代器是constance iterators;multiset与set的唯一区别就是允许存在重复的key值。
  • map则是存放key-value的有序容器,按照key值排序,不允许有重复的key值,key不允许修改但value可以修改;multimap允许存在重复的key值。

由于需要排序,在大多数情况下能够提供快速的插入、删除和查找操作,C++ STL中set、map、multiset、multimapt的实现是通过【红黑树】来实现的。红黑树是一种自平衡的二叉搜索树,它保证了在最坏的情况下,基本操作(插入、删除和查找)的时间复杂度为O(log n)

关于不允许重复值,是通过红黑树中的insert_unique()的API实现的。

6、unordered_map和unordered_set的区别?底层是怎么实现的?

https://yuanbao.tencent.com/bot/app/share/chat/UZ6IpNz557HF

unordered_setunordered_map是两种常见的关联容器,与setmap不同,它们不保证元素的顺序,而是通过哈希表(hash table)来实现的。这使得它们在某些情况下可以提供更快的访问速度。

通过哈希表(hash table,使用链地址法解决冲突)实现的,提供了常数时间复杂度(O(1))的插入、删除和查找操作,这使得unordered_setunordered_map在大多数情况下比setmap更快,特别是在需要频繁访问或修改元素

7、哈希碰撞是什么?怎么解决的?

https://yuanbao.tencent.com/bot/app/share/chat/XasR2YfnIbT6

就是发生在使用哈希表的时候,哈希表保存键值对的时候,由于哈希表的每个位置只能保存一个键值对,这个位置是通过来计算的,键是通过哈希函数来计算的,如果说计算出来这个下标位置相同,则说明的是产生了哈希冲突

解决哈希冲突,我们一般情况下使用链地址法,比如说hashmap就是使用的链地址法来解决的, 哈希表的每个桶(Bucket)维护一个链表。当多个键被哈希到同一位置时,它们会被存储在该桶对应的链表中。

除了链地址法以外还有开放地址法,发生哈希冲突的时候可以使用线性探测,二次探测等方法来找到下一个位置。

还有公共溢出区法 就是将 将冲突元素统一存入独立的溢出区。

8、stl容器迭代器失效的情况、原因、解决方法?

迭代器失效分三种情况考虑,也是分三种数据结构考虑,分别为数组型,链表型,树型数据结构。

  • 数组型数据结构
    • 该数据结构的元素是分配在连续的内存中
    • insert和erase操作,都会使得删除点和插入点之后的元素挪位置,所以,插入点和删除掉之后的迭代器全部失效,也就是说insert(*iter)(或erase(*iter)),然后在iter++,是没有意义的。
    • 解决方法:erase(*iter)的返回值是下一个有效迭代器的值。 iter = cont.erase(iter);
  • 链表型数据结构
    • 对于list型的数据结构,使用了不连续分配的内存
    • 插入不会使得任何迭代器失效
    • 删除运算使指向删除位置的迭代器失效,但是不会失效其他迭代器.
    • 解决办法两种:使用erase方法删除元素时,用其返回值更新迭代器。在调用erase之前,先使用迭代器的后置递增(如iter++)获取下一个有效的迭代器。
  • 树形数据结构
    • 使用红黑树来存储数据
    • 插入不会使得任何迭代器失效
    • 删除运算使指向删除位置的迭代器失效,但是不会失效其他迭代器.
    • erase迭代器只是被删元素的迭代器失效,但是返回值为void所以要采用erase(iter++)的方式删除迭代器

9、sort的底层原理:

C++中的std::sort函数底层实现依赖于三种不同的排序算法的组合:introsort(内省排序),它结合了快速排序堆排序插入排序,以实现高效的排序。它能够处理不同规模的数据,并且在最坏情况下避免了快速排序的性能退化问题。结合了三种算法的优点:

  • 快速排序(Quicksort):在大多数情况下效率非常高,平均时间复杂度为 O(nlog⁡n)
  • 堆排序(Heapsort):在最坏情况下可以保证O(nlog⁡n)的时间复杂度,避免快速排序在极端情况下退化为 O(n^2)
  • 插入排序(Insertion Sort):在处理小数据集时非常高效,时间复杂度为 O(n^2),但由于常数项很小,适合小规模数据。
http://www.dtcms.com/a/617872.html

相关文章:

  • 【Java常用API】----- Math
  • RAG 系统 “检索 - 筛选 - 生成” 完整流程
  • 时间复杂度 和 嵌入式时钟概念 有关系。 我的理由是:时钟经常需要计算频率,而频率往往需要和时间进行计数次数i 。 时间复杂度就像是计数次数i
  • 公司做普通网站建立网站地图
  • Java 大视界 -- Java 大数据在智能农业病虫害精准识别与绿色防控中的创新应用
  • 【高并发架构】从 0 到亿,从单机部署到 K8s 编排:高并发架构的 8 级演进之路
  • 基于Streamlit的交互式3D手指运动学仿真
  • 甘肃做网站找谁金种子酒业网站建设
  • 使用 Flink CDC Elasticsearch Pipeline Connector 打通 MySQL 与 Elasticsearch 的实时链路
  • 基于视频识别的大模型项目实战心得
  • Firefly-Modeler 体积雕刻:AI 概念到 3D 基模
  • 提示词工程 - (2) 指南
  • 网络安全 | 深入理解SQL注入的原理和防范
  • python之循环导入
  • 强杀服务、重启系统及断电对 TDengine 影响
  • Odoo 19 制造与会计集成深度解析报告
  • 免费网站软件正能量医院网站建设方案计划书
  • 软件架构趋势:云原生与大模型的融合与重塑
  • 做网站会员登陆长春网站运做思路
  • 排序java
  • Substance 3D Stager:电商“虚拟摄影”工作流
  • 实验题辅导
  • 【Python TensorFlow】BiTCN-BiLSTM双向时间序列卷积双向长短期记忆神经网络时序预测算法(附代码)
  • 番禺制作网站平台邢台123信息网
  • 网页制作软件有那些石家庄seo网站排名
  • 高级边界扫描 --6-- Silicon Nail测试调试
  • Linux 序列化技术、自定义协议实现及守护进程
  • 【Javaweb学习|黑马笔记|Day5】Web后端基础|java操作数据库
  • ArcGIS地统计综合实战 | 洛杉矶臭氧浓度预测-pretict-pretictable-pretiction
  • 【Agent零基础入门课程】告别黑盒:HelloAgents架构深度解析