vector VS deque

1. vector与deque

vector与动态数组相同,能够在插入或删除元素时自动调整自身大小,其存储由容器自动处理,vector通常占用多于静态数组的空间,因为要分配更多的内存以管理将来的增长,在每次插入元素的时,仅当额外内存耗尽时触发重新分配。

如上图所示,vector元素放置在连续存储中,以便可以使用迭代器访问和遍历他们。在vector中,末尾插入需要不同的时间,因为有时候需要扩展存储空间。对于删除最后一个元素,因为不涉及存储空间大小的调整,则执行时间是恒定的。对于开头或者中间插入和擦除在时间上是线性的,因为可能要涉及到元素的移动。

deque是具有两端扩缩功能的序列容器。其存储方式与vector相反,deque的元素不是相接存储的,是由一段一段等长的连续空间构成的,各段之间并不一定是连续的。它的典型实现如下图所示,通过单独分配固定尺寸的序列(对应图中的数据区),外加额外的登记(对应图中map映射区),map数组中存储的是每段连续空间的地址,通过映射区来管理这些一段一段等长的连续空间,进而实现"整体连续"的效果。

deque容器需要在头部或者尾部增加空间的时候,它会申请一段新的连续空间,同时在map数组的开头或者结尾添加指向该空间的指针,由此将deque元素串接起来。在遇到空间不足的时候由于deque可以申请新的连续空间,原数据空间可以保持不变,更新map即可,所以deque在涉及到空间扩展的时候,效率远高于vector

2. 性能比较

2.1 随机访问

由于vector是连续存储的,deque是分段连续存储,其随机访问需对map数组进行二次指针解引用(可以理解为:deque随机访问需要先去找到待访问元素在哪段连续存储空间,然后再对该空间进行下标访问),而vector只有下标访问一次即可。因此在随机访问性能上,vector略高于deque,但两者复杂度均为常数 O ( 1 ) O(1) O(1)。

2.2 末尾插入/删除

前面我们说过,vector的存储是自动管理的,按需扩张收缩,vector通常占用多于静态数组的空间,因为要分配更多的内存以管理将来的增长,通常情况下vector在尾部插入元素的复杂度为 O ( 1 ) O(1) O(1),但当额外内存耗尽的时候,需要重新分配,此时重新分配,是需要单独再申请一份更大空间,把vector原有的元素重新放到新申请的空间上,再完成尾部插入,此时涉及到了新空间的申请、所有元素的移动和旧空间的释放,这种情况下的插入在性能上是灾难级别的,因此,总的来说对于vector尾部插入的时间复杂度为均摊常数 O ( 1 ) O(1) O(1)

对于deque由于存储空间是分段连续的,当空间不够的时候重新申请新的一段空间即可,不会涉及到旧元素的移动,其复杂度度为常数 O ( 1 ) O(1) O(1)。对于尾部删除,因为不涉及到分配空间申请,因此两者的复杂度均在 O ( 1 ) O(1) O(1)。

vector在尾部插入元素的时,新的size()如果大于capacity(),那么所有的迭代器和引用(包括end()迭代器)都会失效,否则只有end()迭代器会失效。而deque除了迭代器会失效,而不会使指向其余元素的指针或引用失效。

2.3 随机插入/删除

vector在进行随机插入的时候,涉及到插入位置到序列尾部这段元素的移动(可以理解为这段元素需要整体往后移动一位,给新插入元素把位置留出来),随机删除元素同理,因此其随机插入/删除的时间复杂度为插入位置与到vector尾部距离成线性 O ( n ) O(n) O(n)。 deque的扩展方式是双向的,因此其可以**根据插入位置距离头部或者尾部较近的距离成线性 O ( n ) O(n) O(n),**因此,其性能略胜vector一丢丢。

:对于vector随机插入,如果新size()大于旧的capacity()就会导致重分配,所有的迭代器和引用都会失效,否则,只有在插入点前的迭代器和引用会保持有效。对于deque而言,所有迭代器和引用也会失效,除非插入位置为容器尾部或者头部,引用不会失效。

3. 总结

vectordeque的对比如下表所示:

vector deque
头文件 使用需要包含头文件<vector> 使用需要包含头文件<deque>
存储方式 连续存储元素 包含元素连续存储的内存快列表
随机访问 支持,复杂度为常数 O ( 1 ) O(1) O(1) 支持,复杂度为常数 O ( 1 ) O(1) O(1)
末尾插入/末尾删除元素 均摊常数 O ( 1 ) O(1) O(1) 常数 O ( 1 ) O(1) O(1)
随机插入/随机删除元素 与到vector结尾的距离成线性 O ( n ) O(n) O(n) 线性 O ( n ) O(n) O(n)

vector重分配在性能上是有开销的,如果在使用之前元素的数量已知,那么可以使用rederve()函数来消除重分配。

deque的存储按需自动扩展及收缩,扩展deque比扩张vector更优,因为它不涉及到复制既存元素到新内存位置。但另外一方面,deque典型地拥有较大的最小内存开销,所以当即使保有一个元素的时候,deque也需要为它分配它的整个内部数组。

文章首发公众号:iDoitnow如果喜欢话,可以关注一下

相关推荐
做人不要太理性23 分钟前
【C++】深入哈希表核心:从改造到封装,解锁 unordered_set 与 unordered_map 的终极奥义!
c++·哈希算法·散列表·unordered_map·unordered_set
程序员-King.32 分钟前
2、桥接模式
c++·桥接模式
chnming198736 分钟前
STL关联式容器之map
开发语言·c++
程序伍六七1 小时前
day16
开发语言·c++
小陈phd1 小时前
Vscode LinuxC++环境配置
linux·c++·vscode
火山口车神丶1 小时前
某车企ASW面试笔试题
c++·matlab
是阿建吖!2 小时前
【优选算法】二分查找
c++·算法
Ajiang28247353044 小时前
对于C++中stack和queue的认识以及priority_queue的模拟实现
开发语言·c++
‘’林花谢了春红‘’8 小时前
C++ list (链表)容器
c++·链表·list
机器视觉知识推荐、就业指导10 小时前
C++设计模式:建造者模式(Builder) 房屋建造案例
c++