stack和queue
这里stack和queue与其他的容器就有些区别了,前面的string,vector,list他们的模板参数传的都是空间配置器(class Alloc = allocator),这里传的是class Container=deque(deque双端队列),这说明一个问题,栈和队列严格意义上不属于容器了,而是容器适配器(通过容器转换出来的)
stack中的一些接口
queue的一些接口
模拟实现
适配器模式(配接器模式)
-
什么是适配器
适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口
-
stack的模拟实现为了让其更适配它的模板参数是这样的
template<class T,class Container = deque<T>> class stack {};
-
queue的模拟实现的模板参数与stack相同,只不过这里不适合vector了(vector不适合频繁的头插头删,时间复杂度太高)
template<class T,class Container = deque<T>> class queue {};
-
其他接口的实现主要就是靠复用其他容器的函数,根据其特性来实现即可
namespace yyr { template<class T,class Container=deque<T>> class stack { public: stack(const Container& tmp=Container()) :st(tmp) {} void push(const T& val) { st.push_back(val); } void pop() { st.pop_back(); } T& top() { return st.back(); } const T& top() const { return st.back(); } bool empty() const { return st.empty(); } size_t size() const { return st.size(); } private: Container st; }; //这样实现的queue不支持没有头插的容器,比如vector,不过这也符合情况 //队列需要频繁的头插头删,如果使用vector那么时间复杂度将会非常高 template<class T, class Container = deque<T>> class queue { public: queue(const Container& tmp = Container()) :qu(tmp) {} void push(const T& val) { qu.push_front(val); } void pop() { qu.pop_front(); } T& front() { return qu.front(); } const T& front() const { return qu.front(); } T& back() { return qu.back(); } const T& back() const { return qu.back(); } bool empty() const { return qu.empty(); } size_t size() const { return qu.size(); } private: Container qu; }; }
deque
这里对deque进行一个简单的介绍deque是一个双端队列,它是一种双向开口的连续线性空间,所谓双向开口就是可以在头尾两端分别做元素的插入和删除操作,vector虽然也可以头尾两端操作,但是头部操作效率奇差不推荐使用,deque和vector最大的区别就是deque可以进行O(1)的插入删除,而且deque没有所谓容量观念,它是动态地以分段连续空间组合而成(需要中控器来维护),虽然deque也提供了随机访问的迭代器,但是并不是普通的指针,它的迭代器组成十分的复杂。对deque进行排序操作,为了高效率可以将deque中的元素拷到vector中排序完成后再拷贝回来。(图来自于侯捷老师的STL源码剖析)
map(这里的map不是STL里面的map)就可以理解为一个中控的指针数组,他存放的是一段一段的buffer,而buffer里面存放的就是我们所储存的数据
deque作为stack和queue的默认模板参数的优势
-
在stack中,deque对比于vector的优势:
扩容代价不大,不需要拷贝数据,浪费的空间不多,如果是vector当插入的数据刚好超出容量不多,是很浪费空间的
-
在stack/queue中,deque对比于list的优势:
CPU高速cache命中率高。其次不会频繁的申请小块空间,申请和释放空间次数少代价低
总结:
deque作为stack和queue的默认容器是完胜的,它很适合在头尾进行插入删除,但是中间插入删除和随机访问都差强人意,所以要高频随机访问还是需要vector,要任意位置的插入删除还是需要list,deque并不能去替代vector或者list