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如果喜欢话,可以关注一下

相关推荐
yuanbenshidiaos1 分钟前
c++------------------函数
开发语言·c++
yuanbenshidiaos5 分钟前
C++----------函数的调用机制
java·c++·算法
tianmu_sama29 分钟前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
羚羊角uou44 分钟前
【C++】优先级队列以及仿函数
开发语言·c++
姚先生971 小时前
LeetCode 54. 螺旋矩阵 (C++实现)
c++·leetcode·矩阵
FeboReigns1 小时前
C++简明教程(文章要求学过一点C语言)(1)
c语言·开发语言·c++
FeboReigns1 小时前
C++简明教程(文章要求学过一点C语言)(2)
c语言·开发语言·c++
264玫瑰资源库2 小时前
从零开始C++棋牌游戏开发之第二篇:初识 C++ 游戏开发的基本架构
开发语言·c++·架构
_小柏_2 小时前
C/C++基础知识复习(43)
c语言·开发语言·c++
264玫瑰资源库2 小时前
从零开始C++棋牌游戏开发之第四篇:牌桌界面与交互实现
开发语言·c++·交互