问题描述
小F面临一个编程挑战:实现一个基本的计算器来计算简单的字符串表达式的值。该字符串表达式有效,并可能包含数字(0-9)、运算符+、-及括号()。注意,字符串中不包含空格。除法运算应只保留整数结果。请实现一个解析器计算这些表达式的值,且不使用任何内置的eval函数。
难点分析:
-
运算符优先级:
+和-的优先级低于*和/,需要正确处理优先级关系。- 括号
()优先级最高,括号内的表达式应该首先计算。 - 所以需要在处理表达式时,管理运算符的优先级以及括号的解析。
-
括号的嵌套:
- 括号可能嵌套,意味着你需要使用递归或栈来处理括号内的子表达式。
-
整数除法:
- 除法操作需要做整数除法,即忽略小数部分,确保取整。
思路
- 解析表达式:将表达式分解为数字和运算符。
- 处理运算符优先级:使用两个栈,一个用于存储数字,另一个用于存储运算符。
- 处理括号:遇到左括号时,将运算符压入栈;遇到右括号时,弹出运算符直到遇到左括号。
- 计算结果:根据运算符的优先级进行计算,并将结果压入数字栈。
关键步骤
- 解析数字:使用
Character.isDigit判断字符是否为数字,并解析完整的数字。 - 处理括号:遇到左括号时,直接压入运算符栈;遇到右括号时,弹出运算符直到遇到左括号。
- 处理运算符优先级:使用
precedence函数定义运算符的优先级,并根据优先级进行计算。 - 计算结果:使用
applyOperation函数计算两个数的运算结果。
必备知识点:
栈的应用:
-
用途:栈是一种后进先出(LIFO,Last In First Out)的数据结构。在这个问题中,栈用来保存计算中的中间结果,特别是处理括号时。
-
实现细节:当遇到左括号
(时,我们将当前的计算结果和运算符(即符号)压入栈中,遇到右括号)时,弹出栈中的内容以恢复计算状态。这确保了括号内的表达式先被计算。 -
栈操作:
stack.push(x):将x压入栈。stack.pop():弹出栈顶元素。
-
栈的核心作用:用于存储括号前的结果和符号,以便在遇到右括号时,能够恢复计算的状态。
完整代码:
import java.util.Stack;
public class Main {
public static int solution(String expression) {
// 用于存储数字的栈
Stack<Integer> numbers = new Stack<>();
// 用于存储运算符的栈
Stack<Character> operators = new Stack<>();
// 遍历表达式中的每一个字符
for (int i = 0; i < expression.length(); i++) {
char ch = expression.charAt(i);
// 如果是数字,解析完整的数字并压入数字栈
if (Character.isDigit(ch)) {
int num = 0;
// 解析完整的数字
while (i < expression.length() && Character.isDigit(expression.charAt(i))) {
num = num * 10 + (expression.charAt(i) - '0');
i++;
}
i--; // 因为for循环会自动i++,所以这里要减1
numbers.push(num);
}
// 如果是左括号,直接压入运算符栈
else if (ch == '(') {
operators.push(ch);
}
// 如果是右括号,计算直到遇到左括号
else if (ch == ')') {
while (!operators.isEmpty() && operators.peek() != '(') {
// 计算并压入结果
numbers.push(applyOperation(operators.pop(), numbers.pop(), numbers.pop()));
}
operators.pop(); // 弹出左括号
}
// 如果是运算符,处理优先级
else if (ch == '+' || ch == '-' || ch == '*' || ch == '/') {
while (!operators.isEmpty() && precedence(operators.peek()) >= precedence(ch)) {
// 计算并压入结果
numbers.push(applyOperation(operators.pop(), numbers.pop(), numbers.pop()));
}
operators.push(ch);
}
}
// 处理剩余的运算符
while (!operators.isEmpty()) {
numbers.push(applyOperation(operators.pop(), numbers.pop(), numbers.pop()));
}
// 最终结果在数字栈的栈顶
return numbers.pop();
}
// 计算两个数的运算结果
private static int applyOperation(char operator, int b, int a) {
switch (operator) {
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
return a / b; // 注意:这里只保留整数结果
}
return 0;
}
// 定义运算符的优先级
private static int precedence(char operator) {
if (operator == '+' || operator == '-')
return 1;
if (operator == '*' || operator == '/')
return 2;
return 0;
}
public static void main(String[] args) {
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);
}
}