STL详解 - stack与queue的模拟实现
目录
一、容器适配器
1. 什么是适配器模式
2. stack与queue的底层结构
3. deque的原理与缺陷
3.1 deque的原理
3.2 deque的缺陷
4. 为何选择deque作为默认底层容器
二、stack与queue的模拟实现
1. stack的实现
2. queue的实现
一、容器适配器
1. 什么是适配器模式
适配器模式是一种结构型设计模式,其核心思想是将一个类的接口转换为用户期望的另一个接口。在STL中,容器适配器(如stack
和queue
)通过封装现有容器(如deque
、vector
或list
),提供符合特定数据结构特性的接口,从而屏蔽底层细节,简化开发。
2. stack与queue的底层结构
尽管stack
和queue
可以存储元素,但它们并非直接归类为容器,而是容器适配器。这是因为它们基于其他容器进行接口封装,默认情况下使用deque
作为底层容器:
3. deque的原理与缺陷
3.1 deque的原理
deque
(双端队列)是一种伪连续的双开口容器,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。
deque并不是真正连续的空间,其内部由多个固定大小的连续内存块(缓冲区) 组成,通过中控器(指针数组)管理这些块,形成一个逻辑上的连续空间。实际deque类似于一个动态的二维数组,其底层结构如下图所示:
双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图所示:
那deque是如何借助迭代器维护其假想连续的结构呢?
3.2 deque的缺陷
与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是比vector高的。
与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。
但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构。
4. 为何选择deque作为默认底层容器
尽管deque
有遍历效率低的缺陷,但作为stack
和queue
的底层容器却十分合适:
-
无需遍历:
stack
和queue
仅需在固定端操作,规避了deque
的遍历缺陷。 -
高效扩容:相比
vector
,deque
扩容时无需搬移大量元素。 -
内存友好:相比
list
,deque
内存碎片少,空间利用率高。
二、stack与queue的模拟实现
1. stack的实现
stack
使用deque
作为底层容器,默认通过模板参数指定。
push
和pop
操作分别调用底层容器的push_back
和pop_back
。
top
方法返回栈顶元素,直接调用底层容器的back
方法。
size
和empty
方法分别返回底层容器的大小和是否为空。
#include <deque>namespace lv
{template <class T, class Container = std::deque<T>>class stack{public:// 构造函数stack(){}// 向栈顶添加元素void push(const T& x){_container.push_back(x); // 使用底层容器的 push_back}// 从栈顶移除元素void pop(){_container.pop_back(); // 使用底层容器的 pop_back}// 获取栈顶元素T& top(){return _container.back(); // 使用底层容器的 back}// 获取栈顶元素(const版本)const T& top() const{return _container.back();}// 获取栈的大小size_t size() const{return _container.size();}// 判断栈是否为空bool empty() const{return _container.empty();}private:Container _container; // 底层容器,默认使用 deque};
}
2. queue的实现
queue
使用deque
作为底层容器,默认通过模板参数指定。
push
方法调用底层容器的push_back
,将元素添加到队尾。
pop
方法调用底层容器的pop_front
,从队头移除元素。
front
和back
方法分别返回队头和队尾的元素。
size
和empty
方法分别返回底层容器的大小和是否为空。
#include <deque>namespace lv
{template <class T, class Container = std::deque<T>>class queue {public:// 构造函数queue() {}// 向队尾添加元素void push(const T& x) {_container.push_back(x); // 使用底层容器的 push_back}// 从队头移除元素void pop() {_container.pop_front(); // 使用底层容器的 pop_front}// 获取队头元素T& front() {return _container.front(); // 使用底层容器的 front}// 获取队头元素(const版本)const T& front() const {return _container.front();}// 获取队尾元素T& back() {return _container.back(); // 使用底层容器的 back}// 获取队尾元素(const版本)const T& back() const {return _container.back();}// 获取队列的大小size_t size() const {return _container.size();}// 判断队列是否为空bool empty() const {return _container.empty();}private:Container _container; // 底层容器,默认使用 deque};
}