目录
前言
stack
接口介绍
模拟实现
queue
接口介绍
模拟实现
没有迭代器
deque介绍
前言
stack 和 queue 本质上是一种容器配接器,就像我们平时充电时使用的电源适配器,能够将电压转换成设备能够接受的程度。
其通过封装特定容器作为其底层容器的类,通过一组特定的成员函数来实现结构的功能。
stack
🍑stack 就是 STL 中封装好的栈,在使用的时候我们不仅可以指定内部的数据类型,还可以指定内部的容器。
🍑不指定容器其实也是可以的,内部的模板参数有一个缺省值。
int main()
{
stack<int, vector<int>> s1; //内部容器为vector
stack<int, list<int>> s2; //内部容器为list
stack<int> s3; //内部为默认容器deque
return 0;
}
接口介绍
🍑库中还提供了一系列的接口,我们以前如何使用栈就如何使用 stack 即可,下面用一系列代码来示例一下。
int main()
{
stack<int> s;
s.push(5); //5
s.push(6); //5 6
s.push(7); //5 6 7
s.push(8); //5 6 7 8
cout << s.size() << endl; //打印栈的大小
while (!s.empty()) //直到栈为空循环
{
cout << s.top() << endl; //打印栈顶元素
s.pop(); //出栈
}
return 0;
}
模拟实现
🍑根据库中接口简单地实现一下 stack,需要注意的是这里复用的接口需要确保几个底部容器都要同时拥有。
namespace Alpaca
{
template<class T,class Container = deque<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; //容器对象
};
}
queue
🍑queue 则是封装出来的队列,与 stack 相反遵循着先进先出的原则(FIFO)。
🍑通过查询资料我们还发现,要作为 queue 的容器还需要支持以下操作:
🍑同样,使用 queue 时模板参数有两个,分别是内部元素的类型和内部容器,且内部容器缺省为 deque。
int main()
{
queue<int> q1; //传递缺省模板参数
queue<int, list<int>> q2; //指定内部容器为list
return 0;
}
接口介绍
🍑队列与栈相反,其只在队尾入队,在队头出队。我们可以通过实例代码简单使用一下。
int main()
{
queue<int> q;
cout << "size is: " << q.size() << endl; //查看队列大小
q.push(1); //数据入队
q.push(2);
q.push(3);
q.push(4);
q.push(5);
cout << "size is: " << q.size() << endl;
cout << "first is: " << q.front() << endl; //查看首元素
cout << "last is: " << q.back() << endl; //查看最后一个元素
while (!q.empty()) //直到队空循环
{
cout << q.front() << " "; //打印队头
q.pop(); //出队
}
cout << endl;
cout << "size is: " << q.size() << endl; //查看队列大小
return 0;
}
模拟实现
🍑由于是泛型在使用的时候并不会对内部函数进行提示,简单理解就是我们复用对应容器的接口来实现队列的接口函数。
namespace Alpaca
{
template<class T,class Container = deque<T>>
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;
};
}
没有迭代器
🍑因为无论是 stack 和 queue 都是根据其特定的顺序进行元素的插入和删除,因此二者都不提供走访功能,也不提供迭代器。
deque介绍
🍑在出现 vector 和 list 之后,有人想到 vector 能够随机读取,但扩容和随机插入删除效率较低,而 list 插入删除效率都极高却无法随机访问,能不能实现一个结构能够同时兼顾 vector 和 list 的优点呢?
🍑于是 deque 就诞生了,deque 又叫双端队列,即一种双向开口的连续线性空间。
🍑为了兼顾双端插入以及随机访问,deque 的底层是使用一个中控数组来管理一个个连续的空间,且第一个空间被开辟出来后是存放在中控数组的中央位置,之后不断插入数据,若一块连续空间已满只需要再开一块连续空间即可。也就是在中控数组中再增加一个指针。
🍑若是进行头插,则需要开辟一段新空间,将新的值存于连续空间的尾部。
🍑得益于这种结构,即便是扩容所带来的拷贝消耗也是极低的,实际上不会像 vector 那样复杂的深拷贝,而只是对中控数组中的指针进行拷贝。同时 deque 也支持随机访问,只要直到每个连续空间的大小通过简单的计算便可以实现随机访问。
🍑若 deque 有这么多的有点,那为什么它还没有取代 vector 和 list 呢?这就不得不提到 deque 最为鸡肋的地方了,中部的插入删除操作相当复杂,若是直接在中部插入就要挪动当前空间的数据,更甚者还要牵扯到接下来的连续空间。不仅如此 deque 提供的优点不如 vector 和 list 那样极致,这也是为什么 deque 代替不了二者的原因。
🍑但若是有一种容器并不需要中间的插入删除,只需要在两端进行插入删除呢?于是 deque 就成为了适合 stack 和 queue 实现的内部容器。
🍑好了,今天 stack、queue基本使用和模拟实现 的相关内容到这里就结束了,如果这篇文章对你有用的话还请留下你的三连加关注。