题目描述(中等难度)
基础计算器,只有加减乘除,正数,整数。
思路分析
224 题 已经介绍了两种通用的计算器的解法,一种是利用后缀表达式,一种是双栈,这里就直接在 224 题 的基础上改了,大家可以先去做一下。
解法一 后缀表达式
150 题 已经写了后缀表达式的求值,这里的话我们主要是写中缀表达式转后缀表达式,下边是规则。
1)如果遇到操作数,我们就直接将其加入到后缀表达式。
2)如果遇到左括号,则我们将其放入到栈中。
3)如果遇到一个右括号,则将栈元素弹出,将弹出的操作符加入到后缀表达式直到遇到左括号为止,接着将左括号弹出,但不加入到结果中。
4)如果遇到其他的操作符,如(“+”, “-”)等,从栈中弹出元素将其加入到后缀表达式,直到栈顶的元素优先级比当前的优先级低(或者遇到左括号或者栈为空)为止。弹出完这些元素后,最后将当前遇到的操作符压入到栈中。
5)如果我们读到了输入的末尾,则将栈中所有元素依次弹出。
这道题比较简单,不用考虑括号,只需要判断当前操作符和栈顶操作符的优先级。
//op1 > op2 的时候返回 true, 其他情况都返回 false private boolean compare(String op1, String op2) { if (op1.equals("*") || op1.equals("/")) { return op2.equals("+") || op2.equals("-"); } return false; }
下边是整体的代码,供参考。
public int calculate(String s) { String[] polish = getPolish(s); // 转后缀表达式 return evalRPN(polish); } // 中缀表达式转后缀表达式 private String[] getPolish(String s) { List<String> res = new ArrayList<>(); Stack<String> stack = new Stack<>(); char[] array = s.toCharArray(); int n = array.length; int temp = -1; // 累加数字,-1 表示当前没有数字 for (int i = 0; i < n; i++) { if (array[i] == ' ') { continue; } // 遇到数字 if (isNumber(array[i])) { // 进行数字的累加 if (temp == -1) { temp = array[i] - '0'; } else { temp = temp * 10 + array[i] - '0'; } } else { // 遇到其它操作符,将数字加入到结果中 if (temp != -1) { res.add(temp + ""); temp = -1; } // 遇到操作符将栈中的操作符加入到结果中 while (!stack.isEmpty()) { // 栈顶优先级更低就结束 if (compare(array[i]+"",stack.peek())) { break; } res.add(stack.pop()); } // 当前操作符入栈 stack.push(array[i] + ""); } } // 如果有数字,将数字加入到结果 if (temp != -1) { res.add(temp + ""); } // 栈中的其他元素加入到结果 while (!stack.isEmpty()) { res.add(stack.pop()); } String[] sArray = new String[res.size()]; // List 转为 数组 for (int i = 0; i < res.size(); i++) { sArray[i] = res.get(i); } return sArray; } private boolean compare(String op1, String op2) { if (op1.equals("*") || op1.equals("/")) { return op2.equals("+") || op2.equals("-"); } return false; } // 下边是 150 题的代码,求后缀表达式的值 public int evalRPN(String[] tokens) { Stack<String> stack = new Stack<>(); for (String t : tokens) { if (isOperation(t)) { int a = stringToNumber(stack.pop()); int b = stringToNumber(stack.pop()); int ans = eval(b, a, t.charAt(0)); stack.push(ans + ""); } else { stack.push(t); } } return stringToNumber(stack.pop()); } private int eval(int a, int b, char op) { switch (op) { case '+': return a + b; case '-': return a - b; case '*': return a * b; case '/': return a / b; } return 0; } private int stringToNumber(String s) { int sign = 1; int start = 0; if (s.charAt(0) == '-') { sign = -1; start = 1; } int res = 0; for (int i = start; i < s.length(); i++) { res = res * 10 + s.charAt(i) - '0'; } return res * sign; } private boolean isNumber(char c) { return c >= '0' && c <= '9'; } private boolean isOperation(String t) { return t.equals("+") || t.equals("-") || t.equals("*") || t.equals("/"); }
解法二 双栈
规则如下。
- 使用两个栈,
stack0
用于存储操作数,stack1
用于存储操作符 - 从左往右扫描,遇到操作数入栈
stack0
- 遇到操作符时,如果当前优先级低于或等于栈顶操作符优先级,则从
stack0
弹出两个元素,从stack1
弹出一个操作符,进行计算,将结果并压入stack0
,继续与栈顶操作符的比较优先级。 - 如果遇到操作符高于栈顶操作符优先级,则直接入栈
stack1
- 遇到左括号,直接入栈
stack1
。 - 遇到右括号,则从
stack0
弹出两个元素,从stack1
弹出一个操作符进行计算,并将结果加入到stack0
中,重复这步直到遇到左括号
同样的不需要考虑括号,会变得更简单一些。
public int calculate(String s) { char[] array = s.toCharArray(); int n = array.length; Stack<Integer> num = new Stack<>(); Stack<Character> op = new Stack<>(); int temp = -1; for (int i = 0; i < n; i++) { if (array[i] == ' ') { continue; } // 数字进行累加 if (isNumber(array[i])) { if (temp == -1) { temp = array[i] - '0'; } else { temp = temp * 10 + array[i] - '0'; } } else { // 将数字入栈 if (temp != -1) { num.push(temp); temp = -1; } // 遇到操作符 while (!op.isEmpty()) { //遇到更低优先级的话就结束 if (compare(array[i], op.peek())) { break; } // 不停的出栈,进行运算,并将结果再次压入栈中 int num1 = num.pop(); int num2 = num.pop(); num.push(eval(num1, num2, op.pop())); } // 当前运算符入栈 op.push(array[i]); } } if (temp != -1) { num.push(temp); } // 将栈中的其他元素继续运算 while (!op.isEmpty()) { int num1 = num.pop(); int num2 = num.pop(); num.push(eval(num1, num2, op.pop())); } return num.pop(); } private boolean compare(char op1, char op2) { if(op1 == '*' || op1 == '/'){ return op2 == '+' || op2 == '-'; } return false; } private boolean isNumber(char c) { return c >= '0' && c <= '9'; } private int eval(int a, int b, char op) { switch (op) { case '+': return a + b; case '-': return b - a; case '*': return a * b; case '/': return b / a; } return 0; }
和 224 题 一样需要注意减法和除法,由于使用了栈,所以算的时候两个数字要反一下。
解法三
分享一下 这里 的解法,属于专门针对这道题的解法。
把减法、乘法、除法在遍历过程中将结果计算出来,最后将所有结果累加。
public int calculate(String s) { int len; if(s==null || (len = s.length())==0) return 0; Stack<Integer> stack = new Stack<Integer>(); int num = 0; char sign = '+'; for(int i=0;i<len;i++){ if(Character.isDigit(s.charAt(i))){ num = num*10+s.charAt(i)-'0'; } if((!Character.isDigit(s.charAt(i)) &&' '!=s.charAt(i)) || i==len-1){ if(sign=='-'){ stack.push(-num); } if(sign=='+'){ stack.push(num); } if(sign=='*'){ stack.push(stack.pop()*num); } if(sign=='/'){ stack.push(stack.pop()/num); } sign = s.charAt(i); num = 0; } } int re = 0; for(int i:stack){ re += i; } return re; }
总
这道题的话只是抽离了计算器的一部分功能,只要学会了通用的方法,很快就能写出来。