一、题目描述
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 。 整数除法仅保留整数部分。
示例 1:
输入: "3+2*2"
输出: 7
示例 2:
输入: " 3/2 "
输出: 1
示例 3:
输入: " 3+5 / 2 "
输出: 5
说明:
你可以假设所给定的表达式都是有效的。
请不要使用内置的库函数 eval。
二、解题思路
方法一: 先求后缀树,再计算结果,消耗时间空间太高,不理想。
思路:
将表达式(中缀)转化为后缀
将后缀计算出结果
具体规则为:
1.中缀转后缀:
- 数字直接输出到后缀表达式
- 栈为空时,遇到运算符,直接入栈
- 遇到运算符,弹出所有优先级大于或等于该运算符的栈顶元素,并将该运算符入栈
- 将栈中元素依次出栈
例如:表达式:3+2 * 2
遇到3,直接输出到后缀表达式中,栈中元素为空,结果为: 栈: 空; 后缀表达式:3
遇到符号“+”,入栈,结果为: 栈:+ ; 后缀表达式:3
遇到2,直接输出,结果为: 栈:+; 后缀表达式: 3 2
遇到乘号*,入栈,结果为: 栈: * + ;后缀表达式:3 2
遇到2,直接输出,结果为: 栈: * + ;后缀表达式:3 2 2
最后,将元素出栈:结果为:后缀表达式:3 2 2 * +
2.计算后缀:
- 遇到数字,入栈
- 遇到运算符,弹出栈顶两个元素,做运算,并将结果入栈
- 重复上述步骤,直到表达式最右端
例如上述得到的后缀表达式为 3 2 2 * +
3 2 2 都是数字,入栈,结果为:栈:2 2 3
遇到* 号, 2 2 出栈,并计算,2*2 = 4, 4入栈,结果为:栈:4 3 ,表达式还剩一个加号
遇到+ 号,栈顶两个元素出栈并运算,4 3 做加法,4+3 =7
后缀表达式空了,计算完毕,输出结果:7
方法二:和方法一思路不同,但也好理解,代码量少,性能较好,推荐该方法。
思路如下:
先令sign为‘+’,取新符号前处理上一个符号。 eg. 3 * 5 + 2 即· + 3 * 5 + 2 取到 * 时,处理 + 3,然后令sign = * ;取到 + 时,处理 3 * 5,令sign = +;最后 i == s.length() - 1,处理 15 + 2;
三、java代码如下
方法一:
class Solution {
public int calculate(String s) {
return calRst(backTempExp(s));
}
//一、计算输入字符串的后缀表达式
public static LinkedList<String> backTempExp(String exp){
Stack<String> stkEles = new Stack<> ();
LinkedList<String> tempBackExp = new LinkedList<String> ();
for (int i = 0; i < exp.length(); i++){
//1. 遇到了数字
if(Character.isDigit(exp.charAt(i))){
//要处理 多位数,向后遍历到非数字为止
int k = i + 1;
for (; k < exp.length() && Character.isDigit(exp.charAt(k)); k++){
}
tempBackExp.add(exp.substring(i, k));
//将i更新一下,因为在for循环中还要执行 i++
i = k - 1;
continue;
}
//2. 遇到了乘除 运算
if(exp.charAt(i) == '/' || exp.charAt(i) == '*'){
//遇到 优先级大于等于 自己的 就 出栈,加入后缀中
while (!stkEles.isEmpty() && (stkEles.lastElement().equals('/') || stkEles.lastElement().equals('*'))){
tempBackExp.add(stkEles.pop());
}
//将当前字符 入栈
stkEles.push(String.valueOf(exp.charAt(i)));
continue;
}
//3. 遇到了加减 运算
if(exp.charAt(i) == '+' || exp.charAt(i) == '-'){
//遇到 优先级大于等于 自己的 就 出栈,加入后缀中
while (!stkEles.isEmpty() && !Character.isDigit(stkEles.lastElement().charAt(0))){
tempBackExp.add(stkEles.pop());
}
//将当前字符 入栈
stkEles.push(String.valueOf(exp.charAt(i)));
continue;
}
}
//4. 最后弹出栈中所有元素,放入后缀表达式中
while (!stkEles.isEmpty()){
tempBackExp.add(stkEles.pop());
}
return tempBackExp;
}
//二、计算最终结果
public static int calRst(LinkedList<String> tempBackExp){
Stack<Integer> calStk = new Stack<Integer> ();
for (String c : tempBackExp){
//1. 遇到数字 入栈
if (Character.isDigit(c.charAt(0))){
calStk.push(Integer.valueOf(c));
continue;
}
//2. 遇到 加减乘除 运算符, 出栈两个数字元素,进行运算后,将计算结果再入栈
else {
int firstNum = calStk.pop();
int secondNum = calStk.pop();
switch (c.toCharArray()[0]) {
//注意减法和除法时,注意出栈的顺序与原表达式是相反的
case '+':
calStk.push(secondNum + firstNum);
continue;
case '-':
calStk.push(secondNum - firstNum);
continue;
case '*':
calStk.push(secondNum * firstNum);
continue;
case '/':
calStk.push(secondNum / firstNum);
continue;
}
}
}
return calStk.pop();
}
}
方法二:
class Solution {
public int calculate(String s) {
int res = 0, d = 0;
char sign = '+';
Stack<Integer> nums = new Stack<Integer> ();
for (int i = 0; i < s.length(); i++) {
//加减乘除和空格的ASCII码都小于'0'
if (s.charAt(i) >= '0'){
d = d * 10 + s.charAt(i) - '0';
}
if((s.charAt(i) < '0' && s.charAt(i)!=' ') || i == s.length() - 1){
if (sign == '+') {
nums.push(d);
} else if (sign == '-') {
nums.push(-d);
} else if (sign == '*' || sign == '/'){
int temp = sign == '*' ? nums.pop() * d : nums.pop() / d;
nums.push(temp);
}
sign = s.charAt(i);
d = 0;
}
}
for(int t : nums){
res += t;
}
return res;
}
}