【C++】-- STL容器适配器之底层deque浅析

目录

一、deque的使用 

二、deque的原理

1.deque的结构 

 2.deque的底层结构

(1) deque底层空间

 (2)deque如何支持随机访问

(3)deque迭代器 

 3.deque的缺点

(1)deque的优势

(2)deque致命缺陷

4.deque作为stack和queue的底层默认容器的原因


一、deque的使用 

在【C++】-- STL容器适配器之stack一文中介绍了容器适配器的概念,容器适配器是一个封装了序列容器的类模板,对容器进行了转换,转换成栈的后进先出和队列的先进先出的等模板。

虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为stack和队列只是对其他容器的接口进行了包装,STL中stack和queue默认使用deque作为容器:

class template
std::stack
template  > class stack;
class template
std::queue
template  > class queue;

那么deque到底是什么呢?有什么优势使得stack和queue都以它作为底层默认容器,对它进行包装,让stack和queue变成容器适配器的呢?

二、deque的原理

1.deque的结构 

deque作为双端队列,是一种双开口的连续空间的数据结构 ,双开口即可以在头尾两端进行插入和删除,并且时间复杂度为O(1),有没有发现结合了vector和list的优点:

(1)与vector相比,头插O(1),不需要挪动元素

(2)与list相比,连续存储,空间利用率高

【C++】-- STL容器适配器之底层deque浅析_第1张图片

 2.deque的底层结构

(1) deque底层空间

deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成,实际deque类似于一个动态的二维数组,其底层结构如下:【C++】-- STL容器适配器之底层deque浅析_第2张图片

中控指针数组中存放的是指向缓冲区buffer的指针,是动态开辟的二维数组,先malloc一个指针数组,指针数组的每个位置存放一维数组buffer的地址。当deque不断开buffer时,map中控指针数组满了,那么会增容,不过map增容的代价非常低,因为只需要拷贝存储数据的buffer数组的指针,不需要拷贝buffer中的内容。

如果要计算要访问的元素在第几个buffer里面,每个buffer固定大小,i/buffer.size()+1算出在第几个buffer中,i%buffer.size()算出是buffer中的第几个元素。

(2)deque如何支持随机访问

双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂:

【C++】-- STL容器适配器之底层deque浅析_第3张图片

(3)deque迭代器 

那deque是如何借助其迭代器维护其假想连续的结构呢? 如果一个buffer走完了,如何走到下一个buffer的位置呢?用node++来控制,node反向指向map当中当前buffer的位置,那么node++就指向下一个node的位置,解引用就是下一个buffer的位置。

【C++】-- STL容器适配器之底层deque浅析_第4张图片

node并不是从第一个位置就开始存放的,从中间开始存放,那么头插就还有空余位置。

 3.deque的缺点

deque适合头尾插入删除,不适合随机访问,不适合大量中间插入删除,由于它结合了vector和list的特性

【C++】-- STL容器适配器之底层deque浅析_第5张图片

所以其优缺点也跟vector和list相关。

(1)deque的优势

① 与vector比较:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。
② 与list比较:其底层是连续空间,空间利用率比较高,不需要存储额外字段。


(2)deque致命缺陷

不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,不适合大量的头部和中间插入删除,也不适合大量的随机访问。而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构。

4.deque作为stack和queue的底层默认容器的原因

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可 以作为stack的底层容器,比如vector和list都可以;queue是先进先出的特殊线性数据结构,只要具有 push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stack和 queue默认选择deque作为其底层容器,主要是因为:

(1)stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作。

(2)在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,使用deque作为底层默认容器,不仅效率高,而且内存使用率高。

stack和queue结合了deque的优点,而完美的避开了其缺陷。 

你可能感兴趣的:(C++,c++,开发语言)