一、stack的简单介绍和使用
1.1 stack的介绍
1.stack是一种容器适配器,专门用在具有先进后出,后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入和弹出操作。
2.stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器尾部(即栈顶)被压入和弹出。
3.stack的底层容器可以是任何标准的容器类模板或者一些其他待定的容器类,这些容器类需要支持以下的一些操作:
- empty :判空操作
- back:获取尾部元素
- push_back:尾部插入元素
- pop_back:尾部删除元素
4.标准容器vector、deque、list等均符合这些需求,默认情况下没有为stack指定特定的底层容器时,默认情况下就使用deque。
1.2 stack的使用
下面是stack的接口函数及其功能介绍:
函数说明 | 接口说明 |
---|---|
stack() | 构造空的栈 |
empty() | 检测stack是否为空 |
size() | 返回stack中的元素的个数 |
top() | 返回栈顶元素的作用 |
push() | 将元素val压入到stack中 |
pop() | 将stack中栈顶元素弹出 |
下面我们来看下stack在一些问题中的使用:
我们先来看几道算法设计题:
leetcode155,最小栈
对于这个题目我们可以使用两个stack来实现这个最小栈,首先给一个主体栈s和一个辅助栈min_s
里面存储最小值,当我们的元素入栈时如果辅助栈是空的或者val的值小于min_s的栈顶元素就将val入栈。出栈时如果我们主体栈的栈顶元素和我们的辅助栈的栈顶元素相等的话辅助栈的栈顶元素也出栈。取栈顶元素top()就直接返回s.top()即可,取最小值时就直接返回min_s.top()即可。
下面是参考代码:
class MinStack {
public:MinStack() {}void push(int val) {//将val压入主体栈中s.push(val);//如果最小栈为空或者val小于最小栈的栈顶元素就将val也压入进最小栈中if(min_s.empty() || val <= min_s.top()){min_s.push(val);}}void pop() {//出栈时如果主体栈的栈顶元素和最小栈的栈顶元素相同时最小栈也将栈顶元素弹出if(s.top() == min_s.top()){min_s.pop();}s.pop();}//返回主体栈的top即可int top() {return s.top();}//返回最小栈的栈顶元素即可int getMin() {return min_s.top();}
private://定义一个主体栈和一个存放最小值的栈stack<int> s;stack<int> min_s;
};/*** Your MinStack object will be instantiated and called as such:* MinStack* obj = new MinStack();* obj->push(val);* obj->pop();* int param_3 = obj->top();* int param_4 = obj->getMin();*/
牛客网JZ31,栈的压入弹出序列
对于这一题就是一个进栈出栈的一个过程模拟,我们先定义出一个栈然后定义一个维护出栈序列的下标,最后我们将进栈序列中的元素进栈,如果栈不为空并且栈顶元素和出栈元素相同就将栈顶元素出栈,然后将指向出栈序列的popi向后走一步。出循环后如果栈为空就是匹配返回true,如果栈不为空就返回false。
下面是参考代码:
class Solution {
public:/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param pushV int整型vector * @param popV int整型vector * @return bool布尔型*/bool IsPopOrder(vector<int>& pushV, vector<int>& popV) {// write code here//定义一个栈,模拟进栈过程stack<int> s;//定义一个出栈下标,维护出栈序列int popi = 0;//进栈for(auto e : pushV){s.push(e);//如果栈不为空并且出栈队列对应值等于栈顶元素就将栈顶元素弹出,然后将出栈序列向后走一步while(!s.empty() && popV[popi] == s.top()){s.pop();popi++;}}//如果栈为空则匹配否则不匹配return s.empty();}
};
通过这两到例题我们应该也就了解了stack的使用,下面我们来看下queue的用法。
二、queue的简单介绍和使用
2.1 queue的介绍
1.queue也就是队列,也是一种容器适配器,专门用于先进先出的上下文操作中,从容器一端进数据,另一端出数据。
2.queue作为容器适配器实现,容器适配器就是将特定的容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素,元素从队尾入队列,从队头出队列。
3.底层容器可以是标准容器类模版之一,也可以是其他专门设计的容器类。queue的底层容器至少需要支持一下操作:
- empty:判断队列是否为空
- size:返回队列中有效元素中的个数
- front:返回队头元素的引用
- back:返回队尾元素的引用
- push_back:在队列尾部入队列
- pop_back:在队列头部出队列
4. 标准容器类deque和list符合这些要求,默认情况下,如果没有为queue实例化指定容器类,则默认使用deque。
2.2 queue的使用
下面是queue的接口函数以及它的接口功能:
接口函数 | 接口说明 |
---|---|
queue() | 构造空的队列 |
empty() | 检测队列是否为空,为空就放回true,否则返回false |
size() | 返回队列中的有效元素个数 |
front | 返回队头元素的引用 |
back() | 返回队尾元素的引用 |
push() | 将元素进队列 |
pop() | 将元素出队列 |
下面我们通过一个题目来了解一下queue的使用:
leetcode102,二叉树的层序遍历
对于这一题我们可以借助队列queue来解决,我们先定义一个二维数组来存放返回值,然后定义一个queue辅助遍历,定义一个level_size来存放没层的节点,然后先将头结点入队,如果队列不为空就进行外层循环,内层循环负责将遍历到的值放进管理这一层节点的数组v中,然后再将下一层的节点带入,出内层循环后更新level_size和ret数组,即将level_size更新为目前队列中的元素个数,将该层的节点数组v,尾插到ret数组中,最后返回ret数组即可。
参考代码如下:
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left),* right(right) {}* };*/
class Solution {
public:vector<vector<int>> levelOrder(TreeNode* root) {// 定义一个二维数组作为返回数组vector<vector<int>> ret;// 如果二叉树是空树就直接返回空数组if (root == nullptr) {return ret;}// 定义一个辅助队列queue<TreeNode*> q;// 将root入栈q.push(root);// 记录每层的节点数int level_size = 1;//队列不为空就进行循环while (!q.empty()) {//定义一个数组记录每层的节点vector<int> v;//遍历这一层的节点并将下一层的节点带入while (level_size--) {TreeNode* front = q.front();v.push_back(front->val);q.pop();if (front->left) {q.push(front->left);}if (front->right) {q.push(front->right);}}//更新层节点个数level_size = q.size();//将这一层的节点放到返回数组中去ret.push_back(v);}//出循环后返回最终结果return ret;}
};
关于stack和queue的使用这篇博客就讲到这里,大家可以自行通过一些算法题去练习一下,也建议能够对博客里的例题能够理解后去自己实现一下。