【容器适配器】:认识STL中的栈与队列

1,014 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第31天,点击查看活动详情

1、写在前面

大家好,我是翼同学。今天文章的内容是:

  • STL中的栈与队列

2、栈

2.1、简介

栈结构中存储的元素满足先进后出(简称LIFO)的规则,如下所示:

image.png

在STL中,栈往往是归类为容器适配器(container adapter),在栈的内部结构中,可以使用vectordeque等来实现栈的底层逻辑。也就是说,栈适配器stack)是一种单端开口的容器,对外提供的接口就有pushpoptop等,并且需要注意,栈的所有数据元素都必须符合先进后出的规则。而且,栈也不支持遍历操作(也就是不提供迭代器)。


栈的一些概念如下:

  • 栈顶(top):允许进行插入或删除操作的一端,称为栈顶;
  • 栈底(bottom): 与栈顶相对的另一端称为栈底;
  • 入栈(push):在栈顶位置插入数据元素的操作称为入栈(有时也称为进栈、压栈)
  • 出栈(pop):删除栈顶元素的操作称为出栈。(也叫弹栈、退栈)

对于stack适配器来说,每次只能访问位于最顶端的元素,只有移除了栈顶元素后,才能访问下面的元素。

2.2、创建栈

(1) 准备

当我们需要创建栈时,需要导入<stack>头文件,并使用std命名空间。

如下所示:

#include <stack>
using namespace std;

这是因为stack适配器:

  • 以模板类的形式位于<stack>头文件中
  • 并且定义在std命名空间里。

(2) 方式一

举个例子:

stack<int> vals;

在上述代码中,我们创建了一个无元素的stack适配器vals,该stack适配器用于存储int类型的元素,并且底层默认使用deque容器。

因此,如果我们没有指定底层实现,那么创建stack适配器时默认是以deque容器为栈的底层结构(因为deque是一个双向队列,只要封住一端,只开通另一端就可以实现栈的逻辑)。

(3) 方式二

举个例子:

stack<int, vector<int>> vals;

上述代码创建了一个使用vector容器作为底层容器的空stack容器适配器vals。需要注意的是,在指定基础容器时,其存储的数据类型必须和stack容器适配器存储的元素类型保持一致。

<stack>头文件,stack适配器的模板类形式为stack<T, Container=deque<T>>,其中:

  • T:存储元素的数据类型
  • Container:底层容器的类型,可以是vectordequelist

通过模板类的第二个参数Container,我们可以使用除默认容器deque外的其他序列式容器,比如vectorlist

(4) 方式三

举个例子:

list<int> myList {1, 2, 3};             // 定义一个 list 容器
stack<int, list<int>> myStack (myList); // 栈顶元素为 3

在上述代码中,我们使用了一个list容器来初始化一个stack适配器myStack。需要注意,这里使用的容器类型必须和stack所使用的底层容器类型相同,这样才能使用list容器中的数据元素来初始化stack适配器。

(5) 方式四

举个例子:

list<int> vals{100, 200, 300};
stack<int, list<int>> myStack1(vals);
stack<int, list<int>> myStack2 = myStack1;
// 或者也可以这样:stack<int, list<int>> myStack2(myStack1);

在上述代码中,我们使用了一个stack适配器来初始化另一个stack适配器,注意,必须要求二者存储的元素类型以及底层采用的基础容器类型相同。

另外,可以注意到,使用stack适配器给另一个stack适配器进行初始化时,可以有两种方法:

  • stack<int, list<int>> myStack2 = myStack1;
  • 等价于
  • stack<int, list<int>> myStack2(myStack1);

2.3、使用栈

stack容器适配器提供了若干成员函数供我们使用,简单记录如下:

成员函数说明
empty()判断当前是否为空栈(栈中无元素),如果为空栈则返回true,否则返回false
size()获取栈中存储的元素总个数
top()返回栈顶元素的引用,即类型为T&。注意,如果返回空栈的栈顶元素,则程序将报错
push(const T& val)调用底层容器的push_back()函数,将参数val的副本压入栈的顶部,称为新的栈顶元素
push(T&& obj)调用底层容器的有右值引用参数的push_back()函数,将参数压入栈顶
pop()将栈顶元素弹出
swap(stack<T> & other_stack)进行互换操作的两个stack容器适配器中的元素(二者存储的元素类型以及底层采用的容器类型必须相同)

3、队列

前文梳理了一遍栈的特性,事实上,队列的情况也差不多。

3.1、简介

栈结构中存储的元素满足先进先出(简称FIFO)的规则,如下所示:

image.png

stack容器适配器不同,queue容器适配器有2个开放的端口,其中一个用于输入数据,另一个用于输出数据

STL中,队列也被归类为容器适配器(container adapter),而不是容器。同样的,队列也以deque为缺省情况下采用的底层结构。

在队列中,数据元素按照先进先出的规则进行存储,并且同样不允许有遍历的行为(也就不提供迭代器)。

2.2、创建队列

(1) 准备

当我们需要创建队列时,需要导入<queue>头文件,并使用std命名空间。

如下所示:

#include <queue>
using namespace std;

这是因为queue适配器:

  • 以模板类的形式位于<queue>头文件中
  • 并且定义在std命名空间里。

(2) 方式一

举个例子:

queue<int> vals;

在上述代码中,我们创建了一个无元素的queue适配器vals,该queue适配器用于存储int类型的元素,并且底层默认使用deque容器来实现。

因此,如果我们没有指定底层实现,那么创建queue适配器时默认是以deque容器为栈的底层结构。

(3) 方式二

举个例子:

queue<int, vector<int>> vals;

上述代码创建了一个使用vector容器作为底层容器的空queue容器适配器vals。需要注意的是,在指定基础容器时,其存储的数据类型必须和queue容器适配器存储的元素类型保持一致。

<queue>头文件,queue适配器的模板类形式为queue<T, Container=deque<T>>,其中:

  • T:表示存储元素的数据类型
  • Container:表示底层容器的类型,可以是vectordequelist

和栈适配器一样,通过模板类的第二个参数Container,我们可以使用除默认容器deque以外的其他序列式容器,比如vectorlist

(4) 方式三

举个例子:

deque<int> vals {100, 200, 300};
queue<int> myQueue(vals);

在上述代码中,我们使用了一个deque基础容器来初始化一个queue容器适配器myQueue

需要注意,这里使用的容器类型必须和queue所使用的底层容器类型相同,这样才能用deque容器中的数据元素来初始化queue适配器。

(5) 方式四

举个例子:

deque<int> vals{100, 200, 300};
queue<int> myQueue1(vals);
queue<int> myQueue2(myQueue1);
// 或者也可以这样:queue<int> myQueue2 = myQueue1;

在上述代码中,我们使用了一个queue容器适配器myQueue1来初始化另一个queue适配器myQueue2。注意,必须要求二者存储的元素类型以及底层采用的基础容器类型相同。

另外我们可以注意到,一旦使用queue适配器给另一个queue适配器进行初始化时,可以有两种方法:

  • queue<int> myQueue2(myQueue1);
  • 等价于
  • queue<int> myQueue2 = myQueue1;

3.3、使用队列

成员函数说明
empty()判断是否为空队列,如果是则返回true,否则返回false
size()返回queue中元素的个数
front()返回queue中第一个元素的引用。备注:如果queue是常量,就返回一个常引用;如果queue为空,返回值是未定义的
back()返回queue中最后一个元素的引用。备注:如果queue是常量,就返回一个常引用;如果queue为空,返回值是未定义的
push(const T& obj)通过调用底层容器的push_back()函数来将传入参数的副本添加到queue的尾部
push(T&& obj)通过调用底层容器的具有右值引用参数的push_back()函数来将传入的参数以移动的方式添加到queue的尾部
pop()删除queue中的首元素
swap(queue<T> &other_queue)互换两个queue容器适配器中的元素(进行互换操作的两个queue容器适配器存储的元素类型以及底层采用的容器类型必须相同)

3、写在最后

好了,文章的内容就到这里,感谢观看。