难度中等
根据 逆波兰表示法,求表达式的值。
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
注意: 两个整数之间的除法只保留整数部分。
可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:
输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9示例 2:
输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6示例 3:
输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22提示:
1 <= tokens.length <= 104tokens[i]是一个算符("+"、"-"、"*"或"/"),或是在范围[-200, 200]内的一个整数
逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
- 平常使用的算式则是一种中缀表达式,如
( 1 + 2 ) * ( 3 + 4 )。 - 该算式的逆波兰表达式写法为
( ( 1 2 + ) ( 3 4 + ) * )。
逆波兰表达式主要有以下两个优点:
- 去掉括号后表达式无歧义,上式即便写成
1 2 + 3 4 + *也可以依据次序计算出正确结果。 - 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
思路:
这道题其实不难,因为后缀表达式是很适合用栈来实现计算结果的!
步骤:
- 遇到操作数,将该操作数运用 stoi 函数转化为整形,然后入栈
- 遇到操作符,就把栈顶里面的前两个元素拿出来,进行运算,并将结果入栈,以此循环.....
注:其中有一个重点,就是在判断是否为操作符的位置,不能判断该字符串的第一位,而要判断字符串的最后一位(若只有一个字符则就是判断该字符)。
🐛 为什么要这么做?
💡 解答: 如果是判断第一个字符的话,如果该字符串是 “-11”,这本来应该算作数字的,但是如果判断第一个字符的话,会认为这是 减法而导致最后的运算错误!
第一种写法
//第一种写法,用if语句
class Solution {
public:
//因为下面要反复用到,所以写成函数会好一点
//用于提取左右操作数
void OP(stack<int>& st, int& left, int& right)
{
right = st.top();
st.pop();
left = st.top();
st.pop();
}
int evalRPN(vector<string>& tokens) {
stack<int> st;
for(const auto& s : tokens)
{
//分别用于存储左右操作数
int left, right;
//判断一下出现的符号并赋值
if(s.back() == '+')
{
OP(st, left, right);
st.push(left + right);
}
else if(s.back() == '-')
{
OP(st, left, right);
st.push(left - right);
}
else if(s.back() == '*')
{
OP(st, left, right);
st.push(left * right);
}
else if(s.back() == '/')
{
OP(st, left, right);
st.push(left / right);
}
//若为数字的话则将其转化为整形然后push到st中
else
{
st.push(stoi(s));
}
}
return st.top();
}
};第二种写法
//第二种写法,用switch语句
class Solution {
public:
//因为下面要反复用到,所以写成函数会好一点
//用于提取左右操作数
void OP(stack<int>& st, int& left, int& right)
{
right = st.top();
st.pop();
left = st.top();
st.pop();
}
int evalRPN(vector<string>& tokens) {
stack<int> st;
for(const auto& s : tokens)
{
//分别用于存储左右操作数
int left, right;
//判断一下出现的符号并赋值
switch(s.back())
{
case '+':
{
OP(st, left, right);
st.push(left + right);
break;
}
case '-':
{
OP(st, left, right);
st.push(left - right);
break;
}
case '*':
{
OP(st, left, right);
st.push(left * right);
break;
}
case '/':
{
OP(st, left, right);
st.push(left / right);
break;
}
default:
{
st.push((stoi(s)));
break;
}
}
}
return st.top();
}
};第三种写法
class Solution {
public:
int evalRPN(vector<string>& tokens) {
// 利用bao映射运算符与函数的关系
map<string, function<int(int, int)>> hash = {
{"+", [](int a, int b)->int{ return a + b; }},
{"-", [](int a, int b)->int{ return a - b; }},
{"*", [](int a, int b)->int{ return a * b; }},
{"/", [](int a, int b)->int{ return a / b; }}
};
stack<int> st;
for(int i= 0; i < tokens.size(); ++i)
{
if(hash.count(tokens[i]) == 0) // 说明不是运算符
{
st.push(stoi(tokens[i]));
}
else
{
int right = st.top();
st.pop();
int left = st.top();
st.pop();
st.push(hash[tokens[i]](left, right));
}
}
return st.top();
}
};拓展:中缀表达式转化为后缀表达式
其实中缀表达式 ----> 后缀表达式,本质就是调整运算符的优先级
方法:
- 若遇到操作数,则直接输出(或者存在容器中)
- 若遇到操作符,则有多种情况:
- 栈为空,直接入栈
- 栈不为空,且这个操作符比栈顶的操作符的优先级要高,则入栈
- 栈不为空,且这个操作符比栈顶的操作符的优先级要低或者相等,则将栈顶的操作符出栈后输出,然后重新判断该新的操作符,以此循环
- 终止条件:中缀表达式遍历完成,则将栈里的操作符都输出
🐜 有比较特殊的情况,就是如果有括号的情况:
💡 **解答:**设置一个标记,当遇到有括号的时候,让这个标记生效,处理括号里面的操作数,直到遇到括号再将标记取消。