
思路:利用栈实现表达式求值。
1.对于表达式求值的问题,可以维护两个栈,一个nums栈用来记录数字,一个ops栈用来记录操作符,遍历表达式,当遇到操作符时就进行数的相应计算。
2.首先可以定义一个eval()函数,用于从数字栈nums中弹出两个数字a和b,再从操作符栈ops中弹出操作符号,进行计算后将结果数字加入到数字栈nums中。

3.然后从前往后扫描整个表达式:
(1)当遇到空格' '时,跳过。
(2)当遇到数字时,读取一个连续的完整数字,再将其加入到数字栈nums中。
(3)当遇到'+'、'-'、'*'、'/'运算符时,需要考虑优先级,可以用HashMap保存每种操作符的优先级。
(a)如果操作符栈ops的栈顶元素的优先级比当前遇到的操作符的优先级高或者相等,则while()循环进行eval()操作,即将数字栈nums栈顶的两个元素取出来,然后和操作符栈ops的栈顶操作符进行相应的计算,并将计算结果压回数字栈nums中(由于栈的先进后出的特点,后进来的优先级高的操作符会先被用于计算)。
(b)否则,将当前运算符压入操作符栈ops中。
(4)扫描完整个表达式后,如果操作符栈ops中还存在元素,则while循环进行eval()操作。
(5)最终数字栈nums剩下的那个元素就是最终的结果,return返回。

4.细节处理:
(1)由于运算符有优先级,所以设计一个哈希表来存储' + '' - '' * '' / '的优先级,我们将+和-设计为1优先级,将*和/设计为2优先级。
(2)考虑到表达式s的第一个数字可能为负数,因此我们给s开头添加一个字符0。
5.复杂度分析:
(1)时间复杂度:O(n),其中n是字符串s的长度。
(2)空间复杂度:O(n)。
附代码:
java
class Solution {
private LinkedList<Integer> nums = new LinkedList<>();
private LinkedList<Character> ops = new LinkedList<>();
private Map<Character,Integer> map = new HashMap<>();
public int calculate(String s) {
s = '0' + s; // 对开头是负数的处理
map.put('+', 1); //定义运算符的优先级
map.put('-', 1);
map.put('*', 2);
map.put('/', 2);
for(int i = 0; i < s.length();i ++)
{
char c = s.charAt(i);
// 因为乘除法需要立即计算,所以采用的是增强for循环,而不是普通的for i索引for循环
// 逐i位遍历(而非增强for循环的话),每一位都要判断空格(上一题增强for循环无需判断)
if(c == ' ') continue; // 跳过空格
if(c >= '0' && c <= '9') // c是数字,读取一个连续的数字
{
int x = 0;
while(i < s.length() && s.charAt(i) >= '0' && s.charAt(i) <= '9'){
x = x * 10 + s.charAt(i++) - '0';
}
// 抵消循环内最后一次i++造成的额外增量
i--;
nums.push(x);
}
else // c是操作符
{ // ops栈非空并且栈顶操作符优先级大于等于当前操作符c的优先级,进行eval()计算
while(!ops.isEmpty() && map.get(ops.peek()) >= map.get(c)){
// 栈里元素进行计算
eval();
}
// 再把当前操作符c入栈
ops.push(c);
}
}
while(!ops.isEmpty()){
// 栈里元素进行计算
eval();
}
// 所有的计算完毕,数字栈中只剩下了最后一个最终计算结果,return即可
return nums.pop();
}
public void eval()
{
int b = nums.pop();
int a = nums.pop();
char c = ops.pop();
int res = 0;
if(c == '+') res = a + b;
else if(c == '-') res = a - b;
else if(c == '*') res = a * b;
else res = a / b;
// 结果入栈
nums.push(res);
}
}
ACM模式:
java
import java.util.LinkedList;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
class Solution {
private LinkedList<Integer> nums = new LinkedList<>();
private LinkedList<Character> ops = new LinkedList<>();
private Map<Character, Integer> map = new HashMap<>();
public int calculate(String s) {
// 清空之前的数据(防止多次调用)
nums.clear();
ops.clear();
map.clear();
s = '0' + s; // 对开头是负数的处理
map.put('+', 1); // 定义运算符的优先级
map.put('-', 1);
map.put('*', 2);
map.put('/', 2);
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// 因为乘除法需要立即计算,所以采用的是增强for循环,而不是普通的for i索引for循环
// 逐i位遍历(而非增强for循环的话),每一位都要判断空格(上一题增强for循环无需判断)
if (c == ' ') continue; // 跳过空格
if (c >= '0' && c <= '9') { // c是数字,读取一个连续的数字
int x = 0;
while (i < s.length() && s.charAt(i) >= '0' && s.charAt(i) <= '9') {
x = x * 10 + (s.charAt(i) - '0');
i++;
}
// 抵消循环内最后一次i++造成的额外增量
i--;
nums.push(x);
} else { // c是操作符
// ops栈非空并且栈顶操作符优先级大于等于当前操作符c的优先级,进行eval()计算
while (!ops.isEmpty() && map.get(ops.peek()) >= map.get(c)) {
// 栈里元素进行计算
eval();
}
// 再把当前操作符c入栈
ops.push(c);
}
}
while (!ops.isEmpty()) {
// 栈里元素进行计算
eval();
}
// 所有的计算完毕,数字栈中只剩下了最后一个最终计算结果,return即可
return nums.pop();
}
private void eval() {
int b = nums.pop();
int a = nums.pop();
char c = ops.pop();
int res = 0;
if (c == '+') res = a + b;
else if (c == '-') res = a - b;
else if (c == '*') res = a * b;
else res = a / b;
// 结果入栈
nums.push(res);
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取表达式字符串(可能包含空格)
String s = scanner.nextLine();
// 计算结果
Solution solution = new Solution();
int result = solution.calculate(s);
// 输出结果
System.out.println(result);
scanner.close();
}
}