目录
- deque的原理介绍
- deque的优点和缺陷
deque的原理介绍
1.deque(双端队列):是一种双开口的“连续”空间的数据结构,双开口的含义是:在头尾两端都可以进行插入和删除操作,且时间复杂度为O(1)。
需要注意的是deque并不是真正连续的空间,不是像vector那样底层是连续空间的数组,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,为了管理分段空间,deque容器引入了map,称之为中控器,map是一块连续的空间,其中每个元素是指向缓冲区buffer的指针,缓冲区才是deque存储数据的主体,其底层结构如下图所示:
2.双端队列底层是一段假象的连续空间,实际上是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器上,因此deque的迭代器就比较复杂,如下图所示:
中控器包含了map size,指向buffer的指针,deque的开始迭代器与结尾迭代器。
_Tp **_M_map;
size_t _M_map_size;
iterator _M_start;
iterator _M_finish;
deque的优点和缺陷
在了解deque双端队列的优缺点之前,我们先学习一下vector和list的优缺点,有助于更好的比对。
vector的优点:
- 尾插尾删效率很高
- 支持随机访问(利用下标访问)
- 相比链表结构。顺序表CPU高速缓存命中率更高(因为顺序表的底层由数组实现,物理地址是连续的)
vector的缺点:
- 头部和中部插入删除效率低(O(N))
- 扩容:存在性能消耗和空间上的浪费
list的优点:
- 任意位置插入删除效率很高。(O(1))
- 按需申请和释放,优化了vector在空间上的浪费
list的缺点:
- CPU高速缓存的命中率不高(物理地址空间不连续)
- 不支持随机访问
deque的优点:
与vector比较,deque的优势是:头部插入删除时,不需要搬移元素,效率高,而且在扩容时,也不需要搬移大量的元素。因为deque在头部插入删除的时候,是直接在前面开一块空间(缓冲区buffer)插入元素的,尾插同理,在后面开空间。
与list比较,deque的底层是连续空间,空间利用率比较高,不需要存储额外的字段。
deque的缺点:
-
不适合遍历及排序
因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下**,并且deque的迭代器非常复杂,deque维护了两个迭代器,start指向第一个缓冲区的第一个元素,finish指向最后一个缓冲区的最后一个元素的下一个位置,cur指向迭代器所指缓冲区的当前元素,first指向迭代器所指缓冲区第一个元素,last指向迭代器所指缓冲区的最后一个元素,node指向中控,当cur等于last,说明本段空间已被使用完毕,通过node++找到中控数组中下一段内存空间的地址(见下图)。而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用场景并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构。 -
中间插入删除数据不如list
从deque的底层结构图中可以看出,中间插入、删除数据仍会产生数据的挪动。 -
随机访问速度不如vector
由于deque的中控数组中指向的一段地址空间之间并不连续,所以随机访问时需要计算目标数据处于哪段buffer中的第几个数据。计算方式与磁盘定位扇区类似(LBA地址转化为CHS地址)。
为什么选择deque作为stack和queue的底层默认容器?
1.stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作;
2.在stack和queue中,元素增长时,deque和vector相比,deque效率更高(扩容时不需要搬移大量数据),内存使用率更高。