C/C++编程:STL deque原理分析

std::deque ( double-ended queue ,双端队列)是有下标顺序容器,它允许在其首尾两端快速插入及删除。另外,在 deque 任一端插入或删除不会非法化指向其余元素的指针或引用。

deque VS vector

deque和vector非常相似:

  • 它也采用动态数组来管理元素,提供随机访问,有着和vector几乎一模一样的接口

  • 它们都是序列式容器,即元素的位置与插入时机有关,与元素值无关

不同点:vector是单向开口的连续线性空间,deque则是一种双向开口的连续线性空间

  • 所谓双向开口,指的是可以在头尾两端做元素的插入和删除操作
  • vector虽然技术上可以做到双向开口,但是在头部操作的效率非常差,无法被接受

deque的逻辑结构如下:

C/C++编程:STL deque原理分析_第1张图片
为了提供这种能力,deque通常实现为一组独立区块,第一区块向某个方向扩展,最末区块向另一方向扩展。

C/C++编程:STL deque原理分析_第2张图片

  • 因为deque内部是连续分块的空间,因此和vector相比,deque有还有如下不同:
    • 一是:在于deque允许常数级别对头部插入或者删除
    • 二是:deque没有所谓容量(capacity)观念
      • 因为deque是动态的以分段连续空间组合而成,随时可以增加一段新的空间并连接起来。
      • 而vector是”旧空间不足就申请一个更大的空间,然后复制元素,再释放旧空间“。
      • 也因此,deque没有必要提供所谓的空间保留(reserve)功能
    • 另外,虽然deque也提供随机存取迭代器,但是它的迭代器并不是普通指针,其复杂度比vector高太多了,这也影响了各个运算层面。所以,除非必要,我们应该尽可能的选择vector而不是deque

那什么时候用deque呢?

在这里插入图片描述

deque的中控器

deque是连续空间(逻辑上),C++中连续线性空间有array和vector。

  • array无法成长
  • vector虽然可以成长,但是只能向尾端成长,而且其所谓成长其实是个假象。
    • 事实上是(1)另找一个更大空间(2)将原数据复制过去(3)释放原空间三部曲。
    • 如果不是vector每次配置新空间都会留下一些余地,那么代价会非常高
  • deque是有一段一段的定量连续空间构成
    • 一旦有必要再deque的前端或尾端增加空间,就配置一段定量连续空间,串接再整个deque的头部或者尾部
    • deque的最大任务,就是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机存取的接口,避开”重新申请、复制、释放“的三部曲,代价是复杂的迭代器架构。

deque为了维持这个整体连续的假象,设置了一个中控器作为主控。

  • 这个所谓的主控可以看成是一个map(注意不是STL中的map)
  • 这个map是一小块连续空间,其中每个元素节点(node)都是指针,指向另一端较大的连续线性空间,称为缓冲区。
  • 缓冲区才是deque的存储空间主体。
  • STL允许我们指定缓冲区大小,默认值0表示将使用512bytes缓冲区

C/C++编程:STL deque原理分析_第3张图片
C/C++编程:STL deque原理分析_第4张图片

  • 把各种令人头皮发麻的型别定义(为了型别安全,这是必要的)整理一下,我们可以发现,map其实是一个T**。也就是说它是一个指针,所指之物又是一个指针,指向类型为T的一块空间

C/C++编程:STL deque原理分析_第5张图片

deque的迭代器

deque是分段连续空间,维持其”整体连续“假象的任务,落在了迭代器的operator++operator--两个运算子身上。

一个deque迭代器应该满足如下条件:

  • 首先,它必须能够指出分段连续空间(也就是缓冲区)在哪里
  • 其次,它必须能够判断自己是否已经处于其所在缓冲区的边缘
    • 如果是,一旦前进或者后退就必须跳到下一个或者上一个缓冲区。
    • 为了正确跳跃,deque必须随时掌握掌控中心map

C/C++编程:STL deque原理分析_第6张图片

你可能感兴趣的:(C++,leetcode,C++)