问题描述
小F面临一个编程挑战:实现一个基本的计算器来计算简单的字符串表达式的值。该字符串表达式有效,并可能包含数字(0-9)、运算符+、-及括号()。注意,字符串中不包含空格。除法运算应只保留整数结果。请实现一个解析器计算这些表达式的值,且不使用任何内置的eval函数。
测试样例
样例1:
输入:
expression = "1+1"
输出:2
样例2:
输入:
expression = "3+4*5/(3+2)"
输出:7
样例3:
输入:
expression = "4+2*5-2/1"
输出:12
样例4:
输入:
expression = "(1+(4+5+2)-3)+(6+8)"
输出:23
样例5:
输入:
expression = "2*(5+5*2)/3+(6+8*3)"
输出:40
问题分析
我们需要实现一个基本的计算器来计算简单的字符串表达式的值。该字符串表达式有效,并可能包含数字(0-9)、运算符+、-及括号()。注意,字符串中不包含空格。除法运算应只保留整数结果。请实现一个解析器计算这些表达式的值,且不使用任何内置的eval函数。
数据结构选择
-
栈(Stack):
- 用于存储运算符和括号,以便在处理表达式时进行优先级管理。
- 用于存储数字,以便在计算逆波兰表达式时进行操作。
-
字符串(String):
- 用于存储原始表达式。
- 用于构建逆波兰表达式(RPE)。
算法步骤
-
中缀表达式转逆波兰表达式(RPE):
- 遍历输入的字符串表达式。
- 如果当前字符是数字,则将其转换为整数并存储在临时变量中。
- 如果当前字符是左括号
(,则将其压入运算符栈。 - 如果当前字符是运算符(
+、-、*、/),则根据运算符的优先级,将栈顶的运算符弹出并加入到逆波兰表达式中,直到栈为空或遇到优先级更低的运算符。 - 如果当前字符是右括号
),则将运算符栈中的运算符弹出并加入到逆波兰表达式中,直到遇到左括号(。 - 遍历结束后,将栈中剩余的运算符全部弹出并加入到逆波兰表达式中。
-
计算逆波兰表达式:
- 遍历逆波兰表达式。
- 如果当前元素是数字,则将其压入数字栈。
- 如果当前元素是运算符,则从数字栈中弹出两个数字,进行相应的运算,并将结果压入数字栈。
- 遍历结束后,数字栈中剩下的唯一元素即为表达式的计算结果。
具体步骤
-
初始化:
- 创建两个栈:一个用于存储运算符,一个用于存储数字。
- 创建一个字符串构建器(StringBuilder)用于构建逆波兰表达式。
-
中缀转逆波兰:
- 遍历输入表达式,根据字符类型进行相应处理。
- 将数字和运算符按照优先级和括号规则加入到逆波兰表达式中。
-
计算逆波兰表达式:
- 遍历逆波兰表达式,根据元素类型进行相应处理。
- 最终得到计算结果。
总结
通过上述步骤,我们可以将中缀表达式转换为逆波兰表达式,并计算出表达式的值。这个方法利用栈的特性来处理运算符的优先级和括号,确保表达式能够正确计算。
这种类型的题有个秒杀大法,就是使用逆波兰表达式RPE。
代码实现
import java.util.Stack;
public class Main {
public static int solution(String expression) {
// 先转化为RPE
Stack<Character> stack = new Stack<>();
Stack<Integer> numStack = new Stack<>();
StringBuilder vec = new StringBuilder();
int tempNum = 0;
boolean flag = false; // flag 代表tempNum是否被修改
for (int i = 0; i < expression.length(); i++) {
char ch = expression.charAt(i);
if (ch >= '0' && ch <= '9') {
tempNum = tempNum * 10 + (ch - '0');
flag = true;
} else if (ch == '(') {
if (flag) {
vec.append(tempNum).append(" ");
tempNum = 0;
flag = false;
}
stack.push(ch);
} else if (ch == '+' || ch == '-') {
if (flag) {
vec.append(tempNum).append(" ");
tempNum = 0;
flag = false;
}
while (!stack.isEmpty() && (stack.peek() == '+' || stack.peek() == '-' || stack.peek() == '*' || stack.peek() == '/')) {
vec.append(stack.pop()).append(" ");
}
stack.push(ch);
} else if (ch == '*' || ch == '/') {
if (flag) {
vec.append(tempNum).append(" ");
tempNum = 0;
flag = false;
}
while (!stack.isEmpty() && (stack.peek() == '*' || stack.peek() == '/')) {
vec.append(stack.pop()).append(" ");
}
stack.push(ch);
} else if (ch == ')') {
if (flag) {
vec.append(tempNum).append(" ");
tempNum = 0;
flag = false;
}
while (!stack.isEmpty() && stack.peek() != '(') {
vec.append(stack.pop()).append(" ");
}
stack.pop();
}
}
if (flag) {
vec.append(tempNum).append(" ");
tempNum = 0;
flag = false;
}
while (!stack.isEmpty()) {
vec.append(stack.pop()).append(" ");
}
// 执行至此,原中缀表达式已转换为逆波兰表达式并存储至vec中
String[] tokens = vec.toString().split(" ");
for (String token : tokens) {
if (token.equals("+")) {
int ops1 = numStack.pop();
int ops2 = numStack.pop();
int tempAns = ops2 + ops1;
numStack.push(tempAns);
} else if (token.equals("-")) {
int ops1 = numStack.pop();
int ops2 = numStack.pop();
int tempAns = ops2 - ops1;
numStack.push(tempAns);
} else if (token.equals("*")) {
int ops1 = numStack.pop();
int ops2 = numStack.pop();
int tempAns = ops2 * ops1;
numStack.push(tempAns);
} else if (token.equals("/")) {
int ops1 = numStack.pop();
int ops2 = numStack.pop();
int tempAns = ops2 / ops1;
numStack.push(tempAns);
} else { // 是数字
numStack.push(Integer.parseInt(token));
}
}
return numStack.pop();
}
public static void main(String[] args) {
// You can add more test cases here
System.out.println(solution("1+1") == 2);
System.out.println(solution("3+4*5/(3+2)") == 7);
System.out.println(solution("4+2*5-2/1") == 12);
System.out.println(solution("(1+(4+5+2)-3)+(6+8)") == 23);
}
}