实现一个基本的计算器来计算简单的表达式字符串。
表达式字符串只包含非负整数,算符 +
、-
、*
、/
,左括号 (
和右括号 )
。整数除法需要 向下截断 。
你可以假定给定的表达式总是有效的。所有的中间结果的范围均满足 [-231, 231 - 1]
。
注意:你不能使用任何将字符串作为表达式求值的内置函数,比如 eval()
。
示例 1:
输入:s = "1+1" 输出:2
示例 2:
输入:s = "6-4/2" 输出:4
示例 3:
输入:s = "2*(5+5*2)/3+(6/2+8)" 输出:21
提示:
1 <= s <= 10^4
s
由整数、'+'
、'-'
、'*'
、'/'
、'('
和')'
组成s
是一个 有效的 表达式
解法1: 栈
同类题型详解:LeetCode 224. 基本计算器-CSDN博客
解题思路
解决这个问题的关键在于使用两个栈:一个用于存储操作符(opStack
),另一个用于存储操作数(numStack
)。通过模拟运算过程,我们可以逐步计算表达式的值。
算法逻辑
- 初始化两个栈:一个用于存储操作符,另一个用于存储整数。
- 遍历表达式:逐个字符读取表达式字符串。
- 处理数字:当遇到数字时,将其转换成整数并压入
numStack
。 - 处理操作符:
- 如果是左括号'(',压入操作符栈。
- 如果是右括号')',执行操作直到遇到左括号,并将结果压入
numStack
。 - 如果是操作符(+, -, *, /),先执行栈中的操作直到遇到左括号或优先级更低的操作符,然后将当前操作符压栈。
- 执行操作:弹出操作符和相应的操作数,执行相应的运算,并将结果压回
numStack
。 - 最终计算:遍历结束后,执行栈中剩余的操作,得到最终结果。
算法实现步骤
- 定义
calculate
函数,接收一个字符串s
作为参数。 - 定义辅助函数
stackHigher
,用来判断当前操作符是否比栈顶的操作符优先级更高。 - 初始化操作符栈
opStack
和操作数栈numStack
。 - 遍历字符串
s
:- 如果当前字符是数字,将其转换为整数并压入
numStack
。 - 如果是左括号'(',直接压入
opStack
。 - 如果是右括号')',执行操作直到遇到左括号,并将结果压入
numStack
,然后弹出左括号。 - 如果是操作符,先判断是否需要执行栈中已有的操作,然后将当前操作符压入
opStack
。
- 如果当前字符是数字,将其转换为整数并压入
- 处理完所有字符后,继续执行栈中的操作直到
opStack
为空。 - 返回
numStack
的栈顶元素,即为表达式的计算结果。
Java版:
class Solution {
public int calculate(String s) {
Deque<Character> opStack = new ArrayDeque<>();
Deque<Integer> numStack = new ArrayDeque<>();
s = s.replaceAll(" ", "");
int n = s.length();
int i = 0;
while (i < n) {
char c = s.charAt(i);
if (c == '(') {
opStack.push(c);
} else if (c == ')') {
while (!opStack.isEmpty() && opStack.peek() != '(') {
char op = opStack.pop();
int num2 = numStack.pop();
int num1 = numStack.pop();
switch (op) {
case '+':
numStack.push(num1 + num2);
break;
case '-':
numStack.push(num1 - num2);
break;
case '*':
numStack.push(num1 * num2);
break;
case '/':
numStack.push(num1 / num2);
break;
}
}
opStack.pop();
} else if (Character.isDigit(c)) {
int num = 0;
while (i < n && Character.isDigit(s.charAt(i))) {
num = num * 10 + (s.charAt(i) - '0');
i++;
}
i--;
numStack.push(num);
} else {
while (!opStack.isEmpty() && opStack.peek() != '(' && stackHigher(c, opStack.peek())) {
char op = opStack.pop();
int num2 = numStack.pop();
int num1 = numStack.pop();
switch (op) {
case '+':
numStack.push(num1 + num2);
break;
case '-':
numStack.push(num1 - num2);
break;
case '*':
numStack.push(num1 * num2);
break;
case '/':
numStack.push(num1 / num2);
break;
}
}
opStack.push(c);
}
i++;
}
while (!opStack.isEmpty() ) {
char op = opStack.pop();
int num2 = numStack.pop();
int num1 = numStack.pop();
switch (op) {
case '+':
numStack.push(num1 + num2);
break;
case '-':
numStack.push(num1 - num2);
break;
case '*':
numStack.push(num1 * num2);
break;
case '/':
numStack.push(num1 / num2);
break;
}
}
return numStack.peek();
}
private boolean stackHigher(char c, char peek) {
if ((c == '*' || c == '/') && (peek == '+' || peek == '-')) {
return false;
}
return true;
}
}
Python3版:
class Solution:
def calculate(self, s: str) -> int:
def stackHigher(c, peek):
if c in '*/' and peek in '+-':
return False
return True
opStack = []
numStack = []
i = 0
n = len(s)
while i < n:
if s[i] == '(':
opStack.append(s[i])
elif s[i] == ')':
while opStack and opStack[-1] != '(':
op = opStack.pop()
num2 = numStack.pop()
num1 = numStack.pop()
match op:
case '+':
numStack.append(num1 + num2)
case '-':
numStack.append(num1 - num2)
case '*':
numStack.append(num1 * num2)
case '/':
numStack.append(int(num1 / num2))
opStack.pop()
elif s[i].isdigit():
num = 0
while i < n and s[i].isdigit():
num = num * 10 + int(s[i])
i += 1
i -= 1
numStack.append(num)
else:
while opStack and opStack[-1] != '(' and stackHigher(s[i], opStack[-1]):
op = opStack.pop()
num2 = numStack.pop()
num1 = numStack.pop()
match op:
case '+':
numStack.append(num1 + num2)
case '-':
numStack.append(num1 - num2)
case '*':
numStack.append(num1 * num2)
case '/':
numStack.append(int(num1 / num2))
opStack.append(s[i])
i += 1
while opStack:
op = opStack.pop()
num2 = numStack.pop()
num1 = numStack.pop()
match op:
case '+':
numStack.append(num1 + num2)
case '-':
numStack.append(num1 - num2)
case '*':
numStack.append(num1 * num2)
case '/':
numStack.append(int(num1 / num2))
return numStack[-1]
复杂度分析
- 时间复杂度:O(n),其中n是表达式字符串的长度。每个字符只被遍历和处理一次。
- 空间复杂度:O(n),最坏情况下,所有字符都可能被存储在栈中。