模拟实现stack和queue
1. 模拟实现stack
在开始之前,我们需要知道什么是设计模式:设计模式概念 目前有23种。
我们现在接触的模式有两种:适配器模式、迭代器模式
对于迭代器模式,使我们所熟知的,因为对于vector和list的模拟实现,都涉及到迭代器模式,迭代器模式将内部复杂的数据结构进行了封装,从而在上层使用中更为便捷,即不暴露底层细节,封装后提供统一的方式访问容器;而对于适配器模式:现实生活中,被称为适配器的有电源等待,因此适配器本质是已有的东西,封装转换出你想要的东西。对于stack的模拟实现,下面将用适配器转换,vector、list
当然,也可以用list替换vector,效果是相同的,同时缺省容器也要传list
namespace yj
{
// 适配器模式/配接器
template<class T, class Container = vector<T>> //给缺省值,这里给的是缺省容器
class stack
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
const T& top()
{
return _con.back();
}
size_t size()
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
private:
Container _con;
};
}
2. 模拟实现queue
同stack,模拟实现也是采用适配器的方式,因为stack和queue都不存在迭代器。由于queue经常头删,用vector效率低,因此这里最好使用list的适配器。
namespace yj
{
// 适配器模式/配接器
template<class T, class Container = deque<T>> //vector不适合头删,效率低
class queue
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_front();
}
const T& front()
{
return _con.front();
}
const T& back()
{
return _con.back();
}
size_t size()
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
private:
Container _con;
};
}
3. deque的介绍
3.1 deque的原理介绍
deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。
deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示:
双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图所示:
那deque是如何借助其迭代器维护其假想连续的结构呢?
简单来说就是这样一种结构:
2. deque的优缺点
查文档发现,虽然stack和queue用了适配器模式,但是C++库中并不是vector和list,而是deque作为缺省容器这也就对应了deque的优点
优点
- 相比vector,扩容代价低
- 头插头删,尾插尾删的效率高
- 也支持随机访问
为什么选择deque作为stack和queue的底层默认容器?
1.stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作。
2.在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高。
缺点
中间插入元素很麻烦
没有vector和list优点极致
中间插入: