解题文章:实现一个基本的计算器
问题描述
实现一个基本的计算器来计算一个简单的字符串表达式的值。输入是一个字符串表达式,可以包含的运算符有左括号 (、右括号 )、加号 +、减号 -、乘号 * 和除号 /。可以包含的数字为非负整数(小于10)。字符串中不包含空格。处理除法时,直接省略小数部分结果,只保留整数部分参与后续运算。请不要使用内置的库函数 eval。
输入格式
输入是一个字符串表达式,例如:"3+4*5/(3+2)"
数据约束
- 字符串表达式有效。
- 数字为非负整数,且小于10。
输出格式
计算之后的数字。
输入样例
1+1
3+4*5/(3+2)
4+2*5-2/1
(1+(4+5+2)-3)+(6+8)
输出样例
2
7
12
23
解题思路
为了实现一个基本的计算器,我们可以使用栈来处理括号和运算符的优先级。具体步骤如下:
- 解析表达式:遍历字符串,识别数字和运算符。
- 处理括号:使用栈来处理括号内的表达式。
- 处理运算符优先级:使用两个栈,一个用于存储操作数,另一个用于存储运算符。
- 计算结果:根据运算符的优先级进行计算。
代码实现
import java.util.Stack;
public class Main {
public static int solution(String expression) {
Stack<Integer> operands = 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 循环会再加一次
operands.push(num);
} else if (ch == '(') {
operators.push(ch);
} else if (ch == ')') {
while (!operators.isEmpty() && operators.peek() != '(') {
performOperation(operands, operators);
}
operators.pop(); // 弹出 '('
} else if (ch == '+' || ch == '-' || ch == '*' || ch == '/') {
while (!operators.isEmpty() && precedence(operators.peek()) >= precedence(ch)) {
performOperation(operands, operators);
}
operators.push(ch);
}
}
while (!operators.isEmpty()) {
performOperation(operands, operators);
}
return operands.pop();
}
private static void performOperation(Stack<Integer> operands, Stack<Character> operators) {
int b = operands.pop();
int a = operands.pop();
char op = operators.pop();
switch (op) {
case '+':
operands.push(a + b);
break;
case '-':
operands.push(a - b);
break;
case '*':
operands.push(a * b);
break;
case '/':
operands.push(a / b);
break;
}
}
private static int precedence(char op) {
switch (op) {
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
default:
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);
}
}
解释
-
解析表达式:
- 遍历字符串,识别数字和运算符。
- 处理多位数字时,使用一个循环来读取完整的数字。
-
处理括号:
- 当遇到左括号
(时,将其压入运算符栈。 - 当遇到右括号
)时,弹出运算符栈中的运算符并进行计算,直到遇到左括号(。
- 当遇到左括号
-
处理运算符优先级:
- 使用一个栈来存储运算符,并根据运算符的优先级决定是否进行计算。
- 运算符的优先级由
precedence方法定义,乘除运算符优先级高于加减运算符。
-
计算结果:
- 在遍历完表达式后,继续处理运算符栈中的运算符,直到栈为空。
- 最终结果保存在操作数栈中,返回栈顶元素。
复杂度分析
- 时间复杂度:O(n),其中 n 是表达式的长度。每个字符最多被处理两次(一次入栈,一次出栈)。
- 空间复杂度:O(n),最坏情况下,所有字符都入栈。
进阶优化
- 避免多次字符检查:在处理多位数字时,可以使用一个标志位来标记是否正在处理数字,从而减少不必要的字符检查。
- 优化栈操作:在某些情况下,可以使用数组来替代栈,以减少动态内存分配的开销。
- 预处理表达式:对于固定的表达式格式,可以预先解析表达式,提取数字和运算符,减少运行时的解析开销。
结论
通过使用栈来处理括号和运算符的优先级,我们可以有效地实现一个基本的计算器。上述解决方案的时间复杂度为 O(n),空间复杂度为 O(n),适用于大多数简单表达式的计算。对于更复杂的表达式,可以通过进一步优化来提高性能。