【小白爬Leetcode224】2.5 基本计算器 Basic Calculator
Leetcode 224 h a r d \color{#FF0000}{hard} hard
点击进入原题链接:Leetcode224 基本计算器 Basic Calculator
题目
Description
Implement a basic calculator to evaluate a simple expression string.
The expression string may contain open ( and closing parentheses ), the plus + or minus sign -, non-negative integers and empty spaces .
Note:
- You may assume that the given expression is always valid.
- Do not use the eval built-in library function.
中文描述
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式可以包含左括号 ( ,右括号 ),加号 + ,减号 -,非负整数和空格 。
说明:
- 你可以假设所给定的表达式都是有效的。
- 请不要使用内置的库函数 eval。
思路一 状态机(正序读取表达式)
用两个栈num_stack
用来存放操作数字,以及operator_stack
用来存放操作符(+\-)
。
读取字符串的时候,设置一个STATE状态变量:
- 当读取到数字时,
STATE=NUM_STATE
; (注意用于switch判断的判断值应该是const
型) - 读取到空格直接跳过本次循环(注意在
continue
之前要有一个字符串指针后移操作i++
表示已经读取过这个空格,否则会出现死循环) - 读取到其他符号(
( )
和(+\-)
)时,STATE=OPERATOR_STATE
接下来用switch来判断STATE
的状态,进入不同的分支。
用CAL_flag
来记录是否可以进行运算。遇到操作符(+\-)
和右括号')'
时候,CAL_flag=1
,表示遇到下一个数就要进行相应的计算操作,并将结果push进num_stack
中;遇到左括号'('
时,CAL_flag=0
,表示目前不进行运算(而是运算括号内的值)。
class Solution {
public:
//执行计算的函数
void compute(std::stack<int>& num_stack, std::stack<char>& operator_stack) {
if (num_stack.size() < 2) return; //处理"2147483647"这样的特殊情况
char opt = operator_stack.top(); //操作符
int num1 = num_stack.top(); //操作数1
num_stack.pop();
int num2 = num_stack.top(); //操作数2
num_stack.pop();
operator_stack.pop();
if (opt == '+') {
num_stack.push(num2 + num1);
}
else if (opt == '-') num_stack.push(num2 - num1); //特别要注意,由于栈的特性,减数是num1,而被减数反而成了num2。
}
//主体函数
int calculate(string s) {
const int NUM_STATE = 0; //注意用于switch判断的值要是const型
const int OPERATOR_STATE = 1;
int STATE;
int CAL_flag = 0;
int len = s.size();
char cur;
int i=0;
int num;
while (i < len) { //正向遍历字符串
cur = s[i];
if (cur == ' ') {i++;continue;}
if ('0' <= cur && cur <= '9') {
STATE = NUM_STATE;
}
else STATE = OPERATOR_STATE;
switch (STATE) {
case NUM_STATE: {
num = 0;
while (true) { //读取个位数以上的数字
num = 10 * num + (cur - '0');
if (++i < len) cur = s[i];
else break;
if ('0' <= cur && cur <= '9') continue;
else { i--; break; } //如果遇到非数字的char,需要字符串需要向前退一格
}
num_stack.push(num); //将读取到的数字压入栈中
if (CAL_flag == 1) {
compute(num_stack, operator_stack);
}
break;
}
case OPERATOR_STATE: {
if (cur == '+' || cur == '-') {
operator_stack.push(cur);
CAL_flag = 1;
}
else if (cur == '(') CAL_flag = 0;
else { //')'的情况
CAL_flag = 1;
if (!operator_stack.empty()) { //如果操作符栈里还有符号,则是'('前被跳过的那个操作符,执行该运算。
compute(num_stack, operator_stack);
}
}
break;
}
}
i++;
}
return num_stack.top();
}
private:
std::stack<int> num_stack;
std::stack<char> operator_stack;
};
思路二 负号看作正号,括号特殊处理
这个解其实是最简洁最好理解的一个解,附上官方的java代码:以后二刷用C++再写一遍。
它的精髓之处:
- 将加减看作同一种运算,用
sign
的正负1来区分加减; - 用一个
result
和+=
来完成正序的加运算(由于1,不用考虑减运算); - 用
*=
来左括号'('
前的运算。
class Solution {
public int calculate(String s) {
Stack<Integer> stack = new Stack<Integer>();
int operand = 0;
int result = 0; // For the on-going result
int sign = 1; // 1 means positive, -1 means negative
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (Character.isDigit(ch)) {
// Forming operand, since it could be more than one digit
operand = 10 * operand + (int) (ch - '0');
} else if (ch == '+') {
// Evaluate the expression to the left,
// with result, sign, operand
result += sign * operand;
// Save the recently encountered '+' sign
sign = 1;
// Reset operand
operand = 0;
} else if (ch == '-') {
result += sign * operand;
sign = -1;
operand = 0;
} else if (ch == '(') {
// Push the result and sign on to the stack, for later
// We push the result first, then sign
stack.push(result);
stack.push(sign);
// Reset operand and result, as if new evaluation begins for the new sub-expression
sign = 1;
result = 0;
} else if (ch == ')') {
// Evaluate the expression to the left
// with result, sign and operand
result += sign * operand;
// ')' marks end of expression within a set of parenthesis
// Its result is multiplied with sign on top of stack
// as stack.pop() is the sign before the parenthesis
result *= stack.pop();
// Then add to the next operand on the top.
// as stack.pop() is the result calculated before this parenthesis
// (operand on stack) + (sign on stack * (result from parenthesis))
result += stack.pop();
// Reset the operand
operand = 0;
}
}
return result + (sign * operand);
}
}
思路三 栈和反转字符串
来源:官方解答。这道题十分适合用栈来进行。不过这个方法不是很好想,先放上官方的解答mark一下。
class Solution {
public int evaluateExpr(Stack<Object> stack) {
int res = 0;
if (!stack.empty()) {
res = (int) stack.pop();
}
// Evaluate the expression till we get corresponding ')'
while (!stack.empty() && !((char) stack.peek() == ')')) {
char sign = (char) stack.pop();
if (sign == '+') {
res += (int) stack.pop();
} else {
res -= (int) stack.pop();
}
}
return res;
}
public int calculate(String s) {
int operand = 0;
int n = 0;
Stack<Object> stack = new Stack<Object>();
for (int i = s.length() - 1; i >= 0; i--) {
char ch = s.charAt(i);
if (Character.isDigit(ch)) {
// Forming the operand - in reverse order.
operand = (int) Math.pow(10, n) * (int) (ch - '0') + operand;
n += 1;
} else if (ch != ' ') {
if (n != 0) {
// Save the operand on the stack
// As we encounter some non-digit.
stack.push(operand);
n = 0;
operand = 0;
}
if (ch == '(') {
int res = evaluateExpr(stack);
stack.pop();
// Append the evaluated result to the stack.
// This result could be of a sub-expression within the parenthesis.
stack.push(res);
} else {
// For other non-digits just push onto the stack.
stack.push(ch);
}
}
}
//Push the last operand to stack, if any.
if (n != 0) {
stack.push(operand);
}
// Evaluate any left overs in the stack.
return evaluateExpr(stack);
}
}