#227
中等
低频
栈模拟基本计算器 II
这是一道围绕字符串展开的高频练习。建议先掌握「栈模拟」这套写法,再结合下方步骤讲解理解状态维护、边界处理和复杂度取舍。
字符串
题目分析
这道题表面上是在处理「基本计算器 II」,但先要想清楚题目到底让你返回什么,以及过程中哪些约束必须一直满足。从题型上看,它主要在考 字符串 这些能力。先别急着写代码,先把题目要求翻译成人话,明确结果是什么、约束是什么、过程里要持续维护什么。只有先把题目要求翻译成人话,后面的推荐代码才是在实现思路,而不是直接给答案。
接下来怎么看推荐代码: 带着这个理解再看推荐代码时,重点观察这条主线:顺序扫描表达式,遇到
+、- 就把当前数入栈,遇到 *、/ 就立刻和栈顶合并。推荐代码
推荐解法:栈模拟
时间复杂度: O(n)
空间复杂度: O(n)
核心思路: 顺序扫描表达式,遇到
+、- 就把当前数入栈,遇到 *、/ 就立刻和栈顶合并。class Solution {
public int calculate(String s) {
Deque<Integer> stack = new ArrayDeque<>();
int number = 0;
char sign = '+';
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (Character.isDigit(ch)) {
number = number * 10 + (ch - '0');
}
if ((!Character.isDigit(ch) && ch != ' ') || i == s.length() - 1) {
switch (sign) {
case '+':
stack.push(number);
break;
case '-':
stack.push(-number);
break;
case '*':
stack.push(stack.pop() * number);
break;
default:
stack.push(stack.pop() / number);
break;
}
sign = ch;
number = 0;
}
}
int answer = 0;
while (!stack.isEmpty()) {
answer += stack.pop();
}
return answer;
}
}结构化讲解
面试时怎么讲
开场思路
这题我会用栈来处理乘除优先级。
核心过程
- 顺序扫描字符串,累计当前数字。
- 一旦遇到运算符,就根据前一个符号处理这个数字。
- 加减直接带符号入栈,乘除则立刻和栈顶合并。
- 扫描结束后把栈中所有值求和。
复杂度总结
时间复杂度 O(n),空间复杂度 O(n)。
面试补一句:关键点是“乘除即时结算,加减延后结算”。
核心思路
这题只有四则运算且没有括号,关键在于处理乘除优先级。一个稳妥办法是让加减延后结算,乘除在扫描时立即和前一项合并。
步骤讲解
1
逐字符解析数字
连续数字要拼成一个完整整数,同时忽略空格。
为什么这样做:表达式里数字可能不止一位。
对应代码提示:number = number * 10 + (ch - '0');
2
遇到运算符就处理前一个符号
前一个符号决定当前数字如何进入栈,或如何与栈顶合并。
为什么这样做:扫描到新符号时,前一个操作数和运算符已经完整了。
对应代码提示:switch (sign) { ... }
3
最后把栈中元素求和
栈里只剩已经处理好优先级的带符号项,直接累加即可。
为什么这样做:乘除已被即时消化,剩下的只有加减。
对应代码提示:for (int value : stack) answer += value;
易错点
用当前运算符处理当前数字
真正应该生效的是“前一个”运算符。
正确理解:每次在读到新运算符时,处理的是之前累计的数字和旧符号。
整数除法规则写错
Java 这里要求向 0 截断,和题目一致。
正确理解:直接使用 Java 的整数除法即可。
复杂度与适用判断
时间复杂度:O(n)
空间复杂度:O(n)
比其他方案更好在哪里:比手写两个栈的通用表达式求值更轻量,足够应对这题。
适用判断:没有括号、只有乘除优先级差异的表达式题,非常适合这种单栈写法。
额外提醒
- 读到新符号时,才处理上一个符号对应的数字。