1. 题目
给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。
整数除法仅保留整数部分。
示例 1:
输入:s = “3+2*2”
输出:7
示例 2:
输入:s = " 3/2 "
输出:1
示例 3:
输入:s = " 3+5 / 2 "
输出:5
提示:
1 <= s.length <= 3 * 105
s 由整数和算符 (’+’, ‘-’, ‘*’, ‘/’) 组成,中间由一些空格隔开
s 表示一个 有效表达式
表达式中的所有整数都是非负整数,且在范围 [0, 231 - 1] 内
题目数据保证答案是一个 32-bit 整数
Related Topics 栈 字符串
👍 385 👎 0
2. 题解
2.1 解法1: 一个栈
主要思想:
由于只有 加减乘除 四种运算符, 且有优先关系, 可以先进行所有 乘除 运算, 并将运算后的结果放回 栈中, 最后对栈中的数进行 加减 运算, 最终得到结果
算法流程
用一个栈保存数值, 变量 preSign 记录每个数字之前的运算符, 对第一个数字, 运算符记为 +
, 之后遍历字符串的每个字符, 进行如下流程:
- 若 为数字, 则计算该数字的值转化为 整型数 num
- 若读到一个运算符,或者遍历到字符串末尾,即认为是遍历到了数字末尾, 根据 preSign 对 num 以及栈顶元素进行计算, 计算完成后将结果加入栈中, 同时注意处理完该数字后,更新 preSign 为当前遍历的字符。同时将 num置 0
最后遍历完字符串 s 后,将栈中元素累加,即为该字符串表达式的值。
class Solution {
public int calculate(String s) {
Deque<Integer> stack = new ArrayDeque<>();
int num = 0;
char preSign = '+';
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (Character.isDigit(c)) { // 计算整数值
num = num * 10 + c - '0';
}
if ((!Character.isDigit(c) && c != ' ') || i == s.length() - 1) { // 根据运算符计算并入栈
if (preSign == '+') stack.push(num);
else if (preSign == '-') stack.push(-num);
else if (preSign == '*') stack.push(stack.pop() * num);
else if (preSign == '/') stack.push(stack.pop() / num);
preSign = c;
num = 0;
}
}
int ans = 0;
while (!stack.isEmpty()) {
ans += stack.pop();
}
return ans;
}
}
2.2 解法2: 双栈法
一个栈用于保存操作数, 一个栈用于保存运算符
- 使用 map 保存运算符的优先级, 先对字符串进行预处理, 去除空格, 在栈顶加 0
- 遍历字符串的每个字符, 分别按操作数与运算符处理
- 操作数: 使用变量读取连续的数字, 并转化为 Integer型, 存入栈
- 运算符: 判断当前运算符与栈顶运算符的关系, 若当前>=栈顶, 计算栈内的值
- 最后, 将栈内剩余的数进行计算, 返回栈顶值
细节注意点:
- 若当前字符为数字, 获取数值后, 要将下标相应的赋值推进
- 若当前字符为运算符, 栈内运算符优先级 < 当前优先级时, 要退出 while 循环
class Solution {
// 定义一个map, 用于保存运算符优先级
Map<Character, Integer> map = new HashMap<Character, Integer>() {{
put('+', 1);
put('-', 1);
put('*', 2);
put('/', 2);
}};
public int calculate(String s) {
s = s.replaceAll(" ", "");
Deque<Integer> nums = new ArrayDeque<>();
Deque<Character> ops = new ArrayDeque<>();
nums.addLast(0);
char[] cs = s.toCharArray();
for (int i = 0; i < cs.length; i++) {
char c = cs[i];
if (Character.isDigit(c)) {
int curNum = 0;
while (i < cs.length && Character.isDigit(cs[i])) {
curNum = curNum * 10 + (cs[i] - '0');
i++;
}
nums.addLast(curNum);
// 注意最终的 i 需要减 1, 因为for 循环i会 +1
i--;
} else {
// 比较运算符
while (!ops.isEmpty()) {
char prevOp = ops.peekLast();
if (map.get(c) <= map.get(prevOp)) {
compute(nums, ops);
} else {
break;
}
}
ops.addLast(c);
}
}
while (!ops.isEmpty()) {
compute(nums, ops);
}
return nums.peekLast();
}
public void compute(Deque<Integer> nums, Deque<Character> ops) {
if (ops.isEmpty() || nums.size() < 2) {
return;
}
char op = ops.pollLast();
int b = nums.pollLast();
int a = nums.pollLast();
switch (op) {
case '+':
nums.addLast(a + b);
break;
case '-':
nums.addLast(a - b);
break;
case '*':
nums.addLast(a * b);
break;
case '/':
nums.addLast(a / b);
break;
}
}
}