栈的使用以及相关练习题
栈Stack
此处为stack类的模版声明,该模版接受两个类型参数
第一个是class T 代表一个类型,当你实例化stack模版时,可以传一个具体类型,比如int 、double等等。
第二个class Container = deque 是另一个类型,默认值是deque 是一个双端队列容器。如果不给第二个参数传类型,就默认是deque
定义
此处我们可以看到栈的定义
简单的来说,栈是一种容器适配器 ,元素出栈入栈 遵循后入先出的原则。
栈的实例化
stack<int> s1;
stack<double> s2;
stack<string> s3;
相关功能
入栈 push
出栈 pop
将当前的栈顶元素删除 ,不会返回任何数
判空 empty
代码测试
int main()
{
//Test1();
stack<int> s1;
s1.push(1);
s1.push(2);
s1.push(3);
s1.push(4);
s1.push(5);
cout << s1.empty() << endl;
while (!s1.empty())
{
//top()栈顶元素
cout << s1.top() << " ";
//出栈
s1.pop();
}
cout << endl;
cout << s1.empty() << endl;
return 0;
}
结果
栈顶元素top
此处top 返回栈顶元素。除了返回一般类型还返回const类型的
交换swap
交换前
交换后
返回元素个数 size
总代码
#include<iostream>
using namespace std;
#include<stack>
#include<string>
//Stack 基本功能 入栈 出栈 栈顶元素 判空 返回元素个数
void PrintStack( stack<int>& s1)
{
cout << "Print" << endl;
//判空
while (!s1.empty())
{
//top()栈顶元素
cout << s1.top() << " ";
//出栈
s1.pop();
}
cout << endl;
}
void Test1()
{
//stack的实例化
stack<int> s1;
stack<int> s4;
stack<double> s2;
stack<string> s3;
s1.push(1);
s1.push(2);
s1.push(3);
s1.push(4);
s1.push(5);
cout << s1.size() << endl;
s1.pop();
s1.push(6);
s4.push(1);
s4.push(2);
s4.push(3);
//交换
s4.swap(s1);
PrintStack(s1);
PrintStack(s4);
}
int main()
{
Test1();
return 0;
}
练习题
最小栈
https://leetcode.cn/problems/min-stack/
思路解析:
题目 :实现最小栈。入栈,出栈,返回栈顶元素都好说,关键是如何在常数时间内检索到最小元素的栈?
第一想法可能是用一个变量来存最小值,遇见最小值就改变,但是,我们此次实现的栈具有pop功能。如果我们删去的元素恰好就是当前最小元素,那么答案就不对了。因此不可以采用这种方法。
我们可以使用双栈解决此问题。
入栈:一个栈正常存入,另一个栈则存入当前遇见的最小值。正常栈每次存入一个,最小栈只有遇见小于等于栈顶元素的才存入
出栈,正常栈正常出栈,最小栈,只有正常栈等于最小栈元素时才出栈。
top和getmin 就是各自正常返回栈顶元素即可。
代码实现
如下:
class MinStack {
public:
//构造由于有默认构造函数所以不需要写
MinStack() {
}
void push(int val) {
_st.push(val);
if(_minst.empty()||val<=_minst.top())
{
_minst.push(val);
}
}
void pop() {
if(_minst.top()==_st.top())
{
_minst.pop();
}
_st.pop();
}
int top() {
return _st.top();
}
int getMin() {
return _minst.top();
}
//实现最小栈 原栈和最小栈
stack<int> _st;
stack<int> _minst;
};
栈的压入弹出序列
https://www.nowcoder.com/share/jump/6552689871711623746358
思路解析:
如何判断出栈序列是否合法??也许第一想法是看看其中有什么规律,符合这个规律的就是合法的序列,但是,由于入栈序列是无序的,出栈和入栈是不确定的,所以是毫无规律可言的。
那么怎么才是合法的呢?
如果我们可以模拟出这个出栈结果,就是合法的了。
下面,我们定义变量栈st,元素为整型。
定义两个下标,分别指向pushV和popV的起始地址
首先,入栈序列的每个元素入栈,入栈后判断,当前栈顶元素等于当前popi指向的popV中的元素吗?不等于就继续入栈,等于,则出栈并且popi++ 指向下一位,再进行判断,直到不等于,或者栈为空
最后结果返回,直接判断popi是否走到出栈序列最后,没有的话就是没有匹配上
代码实现:
bool IsPopOrder(vector<int>& pushV, vector<int>& popV) {
// write code here
//双指针遍历两个序列
//入栈序列 入栈后再判断是否与出栈序列相等 相等就加加 不等就继续入栈
int pushi = 0,popi = 0;
stack <int> Judg;
while(pushi < pushV.size())
{
//先插入
Judg.push(pushV[pushi++]);
//判断 此处 栈不为空并且 top等于出栈队列的数时 就要执行 不要忘记
while(!Judg.empty()&&Judg.top()==popV[popi])
{
//出栈 popi++
Judg.pop();
popi++;
}
}
//最后结果的判断就是出栈队列是否可以走到头 可以的话 就true
return popi==popV.size();
}
逆波兰表达式求值
https://leetcode.cn/problems/evaluate-reverse-polish-notation/
逆波兰式的算法定义
我们一般常见的是中缀表达式,例如:
a+b+c
后缀表达式形式为 ab+c+
存在意义:可能有人会奇怪,为什么不使用方便的中缀表达式,反而要使用这样复杂的后缀表达式呢?实际上对于我们而言,简单的中缀表达式,对于计算机而言是相当复杂的。
对于计算机而言逆波兰式,是相对简单的,因为遵循着栈先进后出的原则。
逆波兰表达式的算法实现,有兴趣的可以自己实现看看
思路解析:
题目给我们逆波兰表达式,让我们返回最终结果
那么整体思路就是利用栈来存数值,遇到数字就入栈,遇到运算符就出两个元素,然后将运算好的数据进行入栈。
代码实现:
class Solution {
public:
int evalRPN(vector<string>& tokens) {
//逆波兰表达式 存入栈中 数字照常存放 但是遇见操作符需要按照优先级进行入栈
//优先级高的就放进去
stack<int> st;
for(auto& e:tokens)
{
//判断是符号操作数
if(e =="+"||e =="-"||e =="*"||e =="/")
{
int right = st.top();
st.pop();
int left = st.top();
st.pop();
switch (e[0])
{
case '+':
st.push(left+right);
break;
case '-':
st.push(left-right);
break;
case '*':
st.push(left*right);
break;
case '/':
st.push(left/right);
break;
}
}
else
{
//以整型的形式将数字存放进去
st.push(stoi(e));
}
}
return st.top();
}
};