目录
1.容器适配器
2.deque的使用
2.1deque的介绍
2.2deque的缺陷
2.3deque作为stack和queue的可行性
2.4 deque类的使用
2.4.1deque的构造函数
2.4.2deque容量操作
2.4.3deque赋值,插入
1.容器适配器
适配器是一种设计模式(设计模式是一套被人反复使用,经过分类编目的,代码设计经验的的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。
stack的适配器是deque
queue的适配器和stack一样,也是queue
priority_queue的适配器是vector
根据需要进行的操作的不同,我们会在底层选择不同的适配器,比如stack和queue会进行较多的头删和头插操作,底层使用deque比较方便。
2.deque的使用
2.1deque的介绍
deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高
deque的操作图示:
但是deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示:
deque 是由一段一段的定量的连续空间构成。一旦有必要在 deque 前端或者尾端增加新的空间,便配置一段连续定量的空间,串接在 deque 的头端或者尾端。Deque 最大的工作就是维护这些分段连续的内存空间的整体性的假象,并提供随机存取的接口,避开了重新配置空间,复制,释放,代价就是复杂的迭代器架构。
deque 是分段连续内存空间,有中央控制,维持整体连续的假象。中控器中每一个节点都是一个指针,指向真正的缓存区。
双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图所示:
对于迭代器:first指向第一个buf的开始,last指向第一个buf的结束,cur指向buf中的一个数据,node指向存储当前buf指针的中控位置。
class deque
{
iterator start;
iterator finish;
}
start指向第一个buf,cur指向这个buf的第一个数据。
finish指向最后一个buf,cur指向这个buf的最后一个数据的下一个位置。
2.2deque的缺陷
与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。
与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。
但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构。
2.3deque作为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不仅效率高,而且内存使用率高。结合了deque的优点,而完美的避开了其缺陷
2.4 deque类的使用
2.4.1deque的构造函数
构造 | 说明 |
deque<T> deqT; | 默认构造形式 |
deque(beg, end); | 构造函数将[beg, end)区间中的元素拷贝给本身 |
deque(n, elem) | 构造函数将n个elem拷贝给本身。 |
deque(const deque &deq) | 拷贝构造函数 |
2.4.2deque容量操作
操作 | 说明 |
deque.size() | 返回容器中元素的个数 |
deque.empty() | 判断容器是否为空 |
deque.resize(num) | 重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。 |
deque.resize(num, elem) | 重新指定容器的长度为num,若容器变长,则以elem值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除。 |
2.4.3deque赋值,插入
操作 | 说明 |
assign(beg, end) | 将[beg, end)区间中的数据拷贝赋值给本身 |
assign(n, elem) | 将n个elem拷贝赋值给本身 |
deque& operator=(const deque &deq) | 重载等号操作符 |
swap(deq) | 将deq与本身的元素互换 |
push_back(elem) | 在容器尾部添加一个数据 |
pop_back() | 删除容器最后一个数据 |
push_front(elem) | 在容器头部插入一个数据 |
pop_front() | 删除容器第一个数据 |