算法通关村 —— 计算器问题解析
栈有很多经典的问题,前面我们已经学习了像括号匹配这样经典的问题,除此之外,表达式计算和计算器问题天然的适合使用栈来解决,不过,解决起来可能略复杂一些。
计算器问题详细如下: 给定一个字符串表达式s,请你实现一个基本计算器来计算并返回它的值。整数出发仅保留整数部分。 其中假设表达式总是有效的,所有中间结果在[-231. 231-1]的范围内。注意!不允许使用任何将字符串作为数学表达式的内置函数,比如eval();
输入:s = "3+3*3"
输出:12
解决该问题最好的工具就是栈。在计算中先算乘除再算加减,所以我们先进行乘除运算,然后将运算后的整数值返回原表达式的相应位置。此后整个表达式就变成一系列整数加减后的值了。
所以,我们可以先用一个栈来保存乘除运算后的整数值。然后,将加减号后的数字直接压入栈中。最后,对乘除号后的数字,可以直接与栈顶元素计算,并替换栈顶元素为计算后的结果。
总的来说便是去遍历指定的字符串s,并用变量preSign来记录每个数字前的运算符,第一个数字之前的运算符视为加号。每次遍历到数字末尾,则根据preSign来决定计算方式,情况如下:
加号:将数字入栈; 减号:将数字的相反数入栈;
乘除号:计算数字与栈顶元素,并将栈顶元素替换为计算结果
若读到一个运算符,或者遍历到字符串末尾,即认为是遍历到数字末尾,因为我们先计算了乘除,再压加减号的数字入栈,所以后面若遍历到符号则证明已经遍历完。处理完该数字后,更新preSign为当前遍历的字符。
遍历完字符串s后,将栈中元素累加则可得到字符串表达式的值。
具体实现代码如下:
class Solution {
public int calculate(String s) {
// 构造存放计算结果的栈
Deque<Integer> stack = new ArrayDeque<Integer>();
// preSign:标志符号的变量,第一个为加号 num: 存储数字及计算结果入栈
char preSign = '+';
int num = 0, n = s.length();
// 遍历字符串
for(int i = 0; i < n; i++){
// 判断是否为数字
if(Character.isDigit(s.charAt(i))){
// 先入栈的数字为大位的数,所以后面如果又入了数字,则需要前面的数字乘10再相加
num = num*10 + s.charAt(i) - '0';
}
// 若遍历到运算符或者遍历到末尾了
if (!Character.isDigit(s.charAt(i)) && s.charAt(i) != ' ' || i == n - 1) {
switch(preSign){ // 通过switch分情况判断符号执行
case '+': // 加号则直接入栈等待计算
stack.push(num);
break;
case '-': // 符号将相反数入栈
stack.push(-num);
break;
case '*': // 乘号则将栈顶元素出栈进行乘法运算后把结果入栈
stack.push(stack.pop() * num);
break;
default: // 除法同理
stack.push(stack.pop() / num);
}
preSign = s.charAt(i); // 运算符号更新
num = 0; // 计算结果归零
}
}
int ans = 0; // 存储答案值
while(!stack.isEmpty()){
ans += stack.pop(); // 将栈中所有元素出栈相加
}
return ans;
}
// 测试类
public static void main(String[] args) {
System.out.println(calculate( "3+2*2"));
}
}