个人主页:在肯德基吃麻辣烫
分享一句喜欢的话:热烈的火焰,冰封在最沉默的火山深处。
本文章主要介绍容器适配器的功能,以及一个适配的场景。
容器适配器,按字面意思理解的话,就是用来对一个容器进行匹配的。在C++STL中,容器有:vector,list,deque,map,set等。
而在C++STL中不把stack和queue纳入容器的范围而是纳入容器适配器的范围是因为:
stack和queue没有下标随机访问等操作,只有普通的pop_front,push_back,pop_back()等操作,而这些函数在其他容器中完全可以有,栈和队列的实现完全可以将其他容器的操作进行复用,这就是stack和queue作为容器适配器的原因。
至于为什么用deque(双端队列)作为stack和queue的默认适配容器,先看一看stack和queue的基本函数使用。
可以看到在stl库开放的栈的接口中,仅有寥寥无几的几个接口,主要为:push,pop,size,empty,top。
由于栈的后进先出的特性,
这几个函数完全可以复用其他容器的函数。
所以栈其实可以用vector,list等容器进行适配。
下面来模拟实现:
namespace dzt
{
template<class T, class container = deque<T> >
class stack
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
bool empty()
{
return _con.empty();
}
size_t size()
{
return _con.size();
}
T& top()
{
return _con.back();
}
private:
container _con;
};
}
为了和库里面的stack不冲突,这里给了一个命名空间域dzt作为限定。
与stack类似,主要的函数有:
push,pop,front,back,size,empty。
由于队列先进先出的特性
下面来模拟实现queue
namespace dzt
{
template<class T, class container = deque<T> >
class queue
{
public:
void push(const T& x)
{
_con.insert(_con.end(),x);
}
void pop()
{
_con.erase(_con.begin());
}
bool empty() const
{
return _con.empty();
}
size_t size() const
{
return _con.size();
}
//取队头
T& front()
{
return _con.front();
}
//取队尾
T& back()
{
return _con.back();
}
private:
container _con;
};
}
deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。
deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组
deque的底层结构如下:
但是deque也有缺点,虽然它重载了[]访问,但是在访问中间元素时不够极致,需要进行计算。假设给定的下标为i,计算过程如下:
相比于vector,deque的下标访问的效率不够高,
相比于list,deque的中间位置插入删除效率不够高。
本篇文章简单讲述了deque容器的底层原理以及相比于vector和list的缺点,还有用deque作为stack和queue的适配器的原因。