最小栈
https://leetcode.cn/problems/min-stack/https://leetcode.cn/problems/min-stack/
题目要求我们设计一个MinStack
类, 除了提供常见的几个接口之外,还有一个getMin
,使得我们能够在常数时间O(1)内获取到栈中最小的元素。
思路1:MinStack
类里有两个成员:st
就是我们正常定义的要使用的栈,min
是一个辅助栈,用来帮助我们获取最小值。
1.当第一个元素入栈的时候,比如入了一个5,那我们让两个栈都push一个5, 那此时栈中的最小值就是5。
2.然后再入一个4,最小值要更新,那min_st
和st
都push一个4(其实就是保证min_st栈顶的元素一定是最小值,最终我们就可以直接获取):
3.然后如果再入一个6,那最小值不需要更新
那min_st要push入元素吗?可以入也可以不入,入的话再入一个4: 这样min_st栈顶的元素还是最小的。
4.pop的时候,st栈pop的话min栈也pop,两边栈的元素时刻是相同的.
当入栈的新元素小于
min.top()
(或第一次min_st
为空)时,min
入栈这个元素。当入栈的新元素等于
min.top()
时,min
入栈这个元素。当入栈的新元素大于
min.top()
时,min
入栈自己栈顶的元素。出栈的时候,st和min同时出栈
class MinStack {
public:
MinStack()
{}
void push(int val)
{
st.push(val);
if(min.empty())
min.push(val);
else if(val >= min.top())
min.push(min.top());
else
min.push(val);
}
void pop()
{
st.pop();
min.pop();
}
int top()
{
return st.top();
}
int getMin()
{
return min.top();
}
private:
stack<int> st;
stack<int> min;
};
思路2:
前两步和思路一一样,入栈元素小于等于min.top()都入该元素,区别在于入栈元素大于min.top()不入该元素,而且pop也不是同步pop
MinStack
类里有两个成员:st
就是我们正常定义的要使用的栈,min
是一个辅助栈,用来帮助我们获取最小值。
1.当第一个元素入栈的时候,比如入了一个5,那我们让两个栈都push一个5, 那此时栈中的最小值就是5。
2.然后再入一个4,最小值要更新,那min_st
和st
都push一个4(其实就是保证min_st栈顶的元素一定是最小值,最终我们就可以直接获取):
3.然后如果再入一个6,那最小值不需要更新
那min_st要push入元素吗?可以入也可以不入,这里就选择不入
4.pop的时候,st栈正常pop,但只有pop的元素等于当前栈中的最小值即min.top()
,min
才进行pop,否则min
就不pop。
class MinStack {
public:
MinStack()
{}
void push(int val)
{
st.push(val);
if(min.empty() || val <= min.top())
min.push(val);
}
void pop()
{
if(st.top() == min.top())
min.pop();
st.pop();
}
int top()
{
return st.top();
}
int getMin()
{
return min.top();
}
private:
stack<int> st;
stack<int> min;
};
栈的压入、弹出序列
比较简单的一种方法其实就是去模拟这个入栈出栈的过程,
1.首先定义一个栈, 开始入栈.
2.入栈后就开始比较,比较栈顶元素和popV中要出栈的元素是否相等,相等就pop
3.然后并没有结束,有可能st里有多个元素需要一直比较,所以只要st不为空就一直比较然后pop,栈顶元素和出栈元素不相等的话就继续入栈
4.如果最后元素全都入栈了结果还没法pop完(也就是st不为空), 就代表出栈序列是错的, 如果是正确的出栈序列,那最后栈应该是空的.
class Solution {
public:
bool IsPopOrder(vector<int>& pushV, vector<int>& popV)
{
stack<int> st;
int i = 0,j = 0;
while(i < pushV.size())
{
st.push(pushV[i++]);
while(!st.empty() && st.top() == popV[j])
{
j++;
st.pop();
}
}
return st.empty();
}
};
逆波兰表达式求值
这道题目叫做逆波兰表达式求值,那什么是逆波兰表达式呢?
我们正常写的表达式,就比如题目中的这个:(2 + 1) * 3
这种写法叫做中缀表达式,即运算符写在操作数的中间,但是这种写法计算机是不能直接计算的,因为涉及运算符优先级的问题,比如1+2*3,应该先算*。
所以呢,这里就需要我们做一件事情,就是把它变成后缀(逆波兰)表达式,其实就是根据优先级对表达式中的运算符排一个序,并且放到对应的操作数后面。
就比如题目中给的这个示例:((2 + 1) * 3)这个表达式对应的后缀表达式就是["2","1","+","3","*"](题中是把它放到一个字符串数组中了)。即1和2先进行后面的+,得到的结果再和3进行后面的*,得到最终结果。这样就直接从前往后算,不用考虑优先级的问题了。
思路
如果只是解出逆波兰表达式的值, 我们只需要借助一个栈就搞定了。
具体怎么做呢?
1.遍历给的逆波兰表达式对应的字符串数组,
2.如果对应的元素是数字,我们就让该操作数入栈
3.如果遇到操作符,我们就去取栈顶的前两个元素(并pop掉)进行对应的运算
4.然后将结果入栈,后续重复上述操作,最终栈里面唯一的那个元素就是要求的结果。
class Solution {
public:
int evalRPN(vector<string>& tokens)
{
stack<int> st;
for(auto& str : tokens)
{
if(str =="+" || str =="-" ||str =="*" ||str =="/")
{
int right=st.top();
st.pop();
int left=st.top();
st.pop();
switch(str[0])//str是字符串,switch后需要是整型
{
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(str));//字符串转换成int
}
}
return st.top();
}
};
中缀表达式如何转后缀表达式
中缀表达式转后缀表达式_牛客题霸_牛客网 (nowcoder.com)
那现在大家再来思考一个问题:
如果给我们一个中缀表达式,我们如何把它转换成对应的后缀表达式?
那中缀转后缀呢,也是需要借助一个栈,具体怎么做呢?
比如现在有这样一个中缀表达式1+2*3-4
怎么把它转成后缀呢?
1.我们还是从头去遍历这个表达式,如果遇到的是操作数,就输出
2.如果遇到的是的是操作符,那这时要分情况进行分析:
2.1 如果此时栈为空,就让该操作符进栈;
2.2 如果遇到的是操作符,且此时栈不为空,则取栈顶的操作符与当前操作符比较优先级,如果比栈顶操作符优先级高,就让该操作符进栈,为什么是进栈而不是拿它进行运算呢?
因为后面有可能还有优先级更高的,所以先进栈。
3. 继续取下一个进行判断是操作数还是操作符。 如果比栈顶操作符优先级低或者相等,则出栈顶的操作符输出(即此时栈顶的这个操作符可以进行运算了) , 如果优先级更高那就继续入栈
4. 然后再去判断栈是否为空,不为空再拿当前操作符和栈顶操作符比较,进行相应操作,为空就入栈。
5.遍历结束后,如果栈不为空,将剩余操作符输出。
此时,就得到对应的后缀表达式了。
#include <iostream>
#include <stack>
#include <string>
using namespace std;
int Greater(char x, char y)
{
int flag1 = 0, flag2 = 0;
switch (x)
{
case '+':
flag1 = 1;
break;
case '-':
flag1 = 1;
break;
case '*':
flag1 = 2;
break;
case '/':
flag1 = 2;
break;
}
switch (y)
{
case '+':
flag2 = 1;
break;
case '-':
flag2 = 1;
break;
case '*':
flag2 = 2;
break;
case '/':
flag2 = 2;
break;
}
return flag1 - flag2;
}
int main()
{
stack<char> st;
string s,ans;
getline(cin,s);
int i = 0;
while(i<s.length())
{
if(s[i] >='a' && s[i] <='z')//值直接输出
ans += s[i++];
else//运算符
{
if(st.empty())//为空直接入栈
st.push(s[i++]);
else//栈里有运算符
{
while(!st.empty() && Greater(s[i], st.top()) <= 0)//栈不为空且当前运算符优先级小于等于栈里的运算符
{
ans += st.top();
st.pop();
}
if(st.empty() || Greater(s[i], st.top()) == 1)//刚刚可能进行了一系列出栈操作,栈可能为空
{
st.push(s[i]);
i++;
}
}
}
}
while(!st.empty())//最后把栈里运算符全都输出出来
{
ans += st.top();
st.pop();
}
cout << ans;
}
// 64 位输出请用 printf("%lld")
如果是带括号的情况呢?
遇到(
,我们认为它的优先级很低,但是我们不拿(
做比较,直接让它入栈 ,然后下一个操作符也直接让它入栈,那继续往后走遇到)
怎么办,我们也认为)的优先级很低,遇到右括号直接把栈里到左括号为止的操作符全都弹出,再把(pop掉,其实就相当于在()里又进行了一次表达式的转换.