1 总结
想要做出这道题,首先要先搞懂什么是“逆波兰式”,简单点就是中缀表达式转后缀,然后再解决这道题目
2 leetcode 150 逆波兰表达式
总结
1 认识逆波兰表达式
逆波兰表达式又叫做后缀表达式,是一种没有括号,并严格遵循“从左到右”运算的后缀式表达方法,如下表所示:
2 为什么从左到右遍历时,碰到了数字入栈,但是碰到了运算符号就要出栈两个数字呢?
答:首先我们要知道,后缀表达式的意思就是运算符在两个运算数的后面(也是从左到右右遍历波兰式的顺序),但是我们计算的时候要求取得两个运算数和运算符,从左到右遍历会先知道运算数,所以要先存到栈中,当碰到运算符时就可以从栈中取出,如果想要运算这两个数字,就需要利用栈的特性,将数字放到栈中,然后碰到其后面的运算符就需要从栈中取出两个数进行计算然后再放入栈中,然后又碰到了运算符时继续采用上面的步骤,直到遍历到逆波兰式的末尾。由此看,这个就完美的符合了从左到右的运算方法。
3 为什么逆波兰表达式能够考虑到运算符优先级并且将其放到合理的位置?
答:解决这个问题需要看leetcode772的解题过程,这个过程是中缀形式的算术表达式转为后缀的过程,主要依赖了单调栈的特性。
题目
根据 逆波兰表示法,求表达式的值。
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
注意 两个整数之间的除法只保留整数部分。
可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/evaluate-reverse-polish-notation
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new LinkedList<Integer>();
int n = tokens.length;
for (int i = 0; i < n; i++) {
String token = tokens[i];
if (isNumber(token)) {
stack.push(Integer.parseInt(token));
} else {
int num2 = stack.pop();
int num1 = stack.pop();
switch (token) {
case "+":
stack.push(num1 + num2);
break;
case "-":
stack.push(num1 - num2);
break;
case "*":
stack.push(num1 * num2);
break;
case "/":
stack.push(num1 / num2);
break;
default:
}
}
}
return stack.pop();
}
public boolean isNumber(String token) {
return !("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token));
}
}
3 leetcode772 基本计算器3
基本步骤
1 将中缀转后缀
2 计算后缀式
将中缀转后缀
1、首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。
2、读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号“#”。
3、从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。
4、如果不是数字,该字符则是运算符,此时需比较优先关系。
具体做法是:将该字符与运算符栈顶的运算符的优先关系相比较。如果该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。若不是的话,则将栈顶的运算符从栈中弹出,直到栈项运算符的优先级低于当前运算符,将该字符入栈。
5、重复步骤1~2,直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,便可以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。
总结
1 为什么中缀转后缀要使用栈?
答:因为如果中缀表达式中的符号优先级都相同,则无需用到栈直接把数字全部依次左移,符号全部右移都可以,例如a + b - c + d => abcd±+, 其中符号间的相对位置都没有变化,但是如果是a + b * c + d => abc*d++,则发现 * 号的位置前移了,“本来在后面运算符因为优先级不同导致位置前移"符合后进先出的栈的特性 。
2 为什么是单调递增栈而不是递减栈?
答:因为如果优先级大的运算符再前面小的在后面就只需要按照正常顺序转就好了,比如a * b + c + d => ab*cd++。 所以只有当优先级大的运算符在后面小的在前面这种情况才需要用到栈的后进先出特性将优先级大的运算符放在合适的位置。
题目
实现一个基本的计算器来计算简单的表达式字符串。
表达式字符串只包含非负整数,算符 +、-、*、/ ,左括号 ( 和右括号 ) 。整数除法需要 向下截断 。
你可以假定给定的表达式总是有效的。所有的中间结果的范围均满足 [-231, 231 - 1] 。
注意:你不能使用任何将字符串作为表达式求值的内置函数,比如 eval() 。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/basic-calculator-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码
class Solution {
public int calculate(String s) {
List<String> tokens = getRPN(s);
return evalRPN(tokens);
}
public List<String> getRPN(String s) {
Deque<Character> st = new LinkedList<>();
List<String> rpn = new ArrayList<>();
int len = s.length();
int num = 0;
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
if (Character.isDigit(c)) {
num = num * 10 + c - '0';
} else {
if (i > 0 && Character.isDigit(s.charAt(i - 1))) {
rpn.add(String.valueOf(num));
num = 0;
}
if (c == '(') {
st.offerLast(c);
} else if (c == ')') {
while (st.peekLast() != '(') {
rpn.add(String.valueOf(st.pollLast()));
}
st.pollLast();
} else {
// 如果是 +, -, *,/等运算符中的一个,则
int precedence = getPrecedence(c);
while (!st.isEmpty() && getPrecedence(st.peekLast()) >= precedence) {
rpn.add(String.valueOf(st.pollLast()));
}
st.offerLast(c);
}
}
}
if (Character.isDigit(s.charAt(len - 1))) {
rpn.add(String.valueOf(num));
}
while (!st.isEmpty()) {
rpn.add(String.valueOf(st.pollLast()));
}
return rpn;
}
public int evalRPN(List<String> tokens) {
Deque<Integer> stack = new ArrayDeque<Integer>();
int size = tokens.size();
for (int i = 0; i < size; i++) {
String token = tokens.get(i);
if (isNumber(token)) {
stack.push(Integer.parseInt(token));
} else {
int num2 = stack.pop();
int num1 = stack.pop();
switch (token) {
case "+":
stack.push(num1 + num2);
break;
case "-":
stack.push(num1 - num2);
break;
case "*":
stack.push(num1 * num2);
break;
case "/":
stack.push(num1 / num2);
break;
default:
}
}
}
return stack.pop();
}
public int getPrecedence(char op) {
if (op == '*' || op == '/') {
return 2;
} else if (op == '+' || op == '-') {
return 1;
} else {
return 0;
}
}
public boolean isNumber(String token) {
return Character.isDigit(token.charAt(token.length() - 1));
}
}