150. Evaluate Reverse Polish Notation
简单又经典的一道题,注意一下string和int的类型就行了。
这道逆波兰表达式里,每一个数字都是string存在字符串数组里面,也就是说一个multiple digits的数字也不用特殊处理,但不是每道题都能这么方便,遇到char需要手动加空格(padding)处理。
06/16更新:Integer.parseInt(str) 熟悉接口很重要啊,不然要自己写,还容易出错。转换int的时候都忘了考虑负数了。
public class Solution {
public int evalRPN(String[] tokens) {
if (tokens == null || tokens.length == 0) return -1;
Stack<Integer> stack = new Stack<>();
for (String str : tokens) {
if (str.equals("+")) {
stack.push(stack.pop() + stack.pop());
} else if (str.equals("-")) {
int sec = stack.pop();
int fir = stack.pop();
stack.push(fir - sec);
} else if (str.equals("*")) {
stack.push(stack.pop() * stack.pop());
} else if (str.equals("/")) {
int sec = stack.pop();
int fir = stack.pop();
stack.push(fir / sec);
} else {
stack.push(Integer.parseInt(str));
}
}
return stack.pop();
}
}
224. Basic Calculator
计算器类问题,离不开 Dijkstra 的 Shunting-yard algorithm 和 reverse polish notation.
Input: 3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3
因为有负号,运算顺序不能颠倒;
数字可能是多数位的,也可能是0,不能 assume 都是一位数;
带括号的处理方式;
复现了一下 Dijkstra 的 shunting yard algorithm,因为只有 “+” 和 “-” ,运算符优先级上要简单一些。在这个代码基础上扩展任何新的运算符都很容易,加个判断函数就行了。
建个 StringBuilder 存输出的 RPN,一个 Stack 存运算符;
如果看到数字,直接添加到 StringBuilder 中;
如果看到 "(" ,直接添加到 Stack 上;
如果看到 ")",把 Stack 上的所有运算符 pop 出来添加到 StringBuilder,直到遇见 "(" 为止;
(本题用不上)如果看到运算符,把 Stack 上所有 "大于等于当前运算符优先级" 的 pop 出来加到 StringBuilder,最后把自己放入 Stack,此时要么 Stack 为空,要么 Stack.peek() 的优先级比当前运算符小。
优先级 :"乘除 = 3", "加减 = 2”
这道题的核心就是怎么把一个数学表达式转换成逆波兰表达式,最后再用逆波兰计算。
关键是multiple digits,带空格,有零,这些corner case,转换成char再转换string之间的处理。并不难,但要写出bug free需要一定的练习。
06/19更新:这道题和LC71有一个细节是一样的,就是对于空字符串的处理,很显然你要考虑跳过空字符串,不然会出错,但我觉得这个细节很神奇在于这些空字符串是怎么来的呢?
public class Solution {
public int calculate(String s) {
StringBuilder sb = new StringBuilder();
Stack<Character> stack = new Stack<>();
for (int i = 0; i < s.length(); ++i) {
char ch = s.charAt(i);
if (ch == ' ') continue;
if (Character.isDigit(ch)) {
sb.append(ch);
} else {
sb.append(' ');
if (ch == '(') {
stack.push(ch);
} else if (ch == ')') {
while (!stack.isEmpty() && stack.peek() != '(') {
sb.append(stack.pop());
sb.append(' ');
}
stack.pop();
} else {
while(!stack.isEmpty() && stack.peek() != '('){
sb.append(stack.pop());
sb.append(' ');
}
stack.push(ch);
}
}
}
while(!stack.isEmpty()) {
sb.append(' ');
sb.append(stack.pop());
}
return rePolish(sb.toString());
}
private int rePolish(String s){
String[] strs = s.split(" ");
Stack<Integer> stack = new Stack<>();
for(String str : strs){
if(str.equals("")) continue;
if("+-".indexOf(str) != -1){
int num2 = stack.pop();
int num1 = stack.pop();
if(str.equals("+")) stack.push(num1 + num2);
if(str.equals("-")) stack.push(num1 - num2);
} else {
stack.push(Integer.parseInt(str));
}
}
return stack.pop();
}
}
227. Basic Calculator II
思路是一样的,两点注意:
char类型要用==和!=,string要用.equal(),总忘。
最后stack清空的时候,padding在前,sb.append(' ')在pop之前。
public class Solution {
public int calculate(String s) {
if (s == null || s.length() == 0) return 0;
Stack<Character> stack = new Stack<>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); ++i) {
char ch = s.charAt(i);
if (ch == ' ') continue;
if (Character.isDigit(ch)) {
sb.append(ch);
} else {
sb.append(' ');
if (ch == '(') {
stack.push(ch);
} else if (ch == ')') {
while (!stack.isEmpty() && stack.peek() != '(') {
sb.append(stack.pop());
sb.append(' ');
}
stack.pop();
} else {
while (!stack.isEmpty() && getPrecedence(ch) <= getPrecedence(stack.peek())) {
sb.append(stack.pop());
sb.append(' ');
}
stack.push(ch);
}
}
}
while (!stack.isEmpty()) {
sb.append(' ');
sb.append(stack.pop());
}
return rePolish(sb.toString());
}
private int rePolish(String s) {
String[] strs = s.split(" ");
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < strs.length; ++i) {
String str = strs[i];
if (str.equals("+")) {
stack.push(stack.pop() + stack.pop());
} else if (str.equals("-")) {
int num2 = stack.pop();
int num1 = stack.pop();
stack.push(num1 - num2);
} else if (str.equals("*")) {
stack.push(stack.pop() * stack.pop());
} else if (str.equals("/")) {
int num2 = stack.pop();
int num1 = stack.pop();
stack.push(num1 / num2);
} else {
stack.push(Integer.parseInt(str));
}
}
return stack.pop();
}
private int getPrecedence(char ch) {
if (ch == '*' || ch == '/') return 3;
if (ch == '+' || ch == '-') return 2;
return 0;
}
}
439. Ternary Expression Parser
根据规则,得从后面先判断。因为数字只有一位,所以pop出来的必定是符号"?",第一个数,符号":",第二个数。再根据前面的tf push进结果。
public String parseTernary(String expression) {
if (expression == null || expression.length() == 0) return "";
Deque<Character> stack = new LinkedList<>();
for (int i = expression.length() - 1; i >= 0; i--) {
char c = expression.charAt(i);
if (!stack.isEmpty() && stack.peek() == '?') {
stack.pop(); //pop '?'
char first = stack.pop();
stack.pop(); //pop ':'
char second = stack.pop();
if (c == 'T') stack.push(first);
else stack.push(second);
} else {
stack.push(c);
}
}
return String.valueOf(stack.peek());
}