849 · Basic Calculator III
Algorithms
Hard
Description
Implement a basic calculator to evaluate a simple expression string.
The expression string contains only non-negative integers, +, -, *, / operators , open ( and closing parentheses ) and empty spaces . The integer division should truncate toward zero.
You may assume that the given expression is always valid. All intermediate results will be in the range of [-2147483648, 2147483647]
Do not use the eval built-in library function.
Example
Example 1:
Input:“1 + 1”
Output:2
Explanation:1 + 1 = 2
Example 2:
Input:" 6-4 / 2 "
Output:4
Explanation:4/2=2,6-2=4
Tags
Company
Microsoft
Related Problems
978
Basic Calculator
Medium
980
Basic Calculator II
Medium
981
Basic Calculator IV
Hard
解法1:利用栈和递归。遇到括号就递归。这个方法比较好。
class Solution {
public:
/**
* @param s: the given expression
* @return: the result of expression
*/
int calculate(string &s) {
int index = 0;
return calExpr(s, index);
}
private:
int calNum(string &s, int &index) {
int num = 0;
while (index < s.size() && isdigit(s[index])) {
num = num * 10 + (s[index] - '0');
index++;
}
return num;
}
int calExpr(string &s, int &index) {
int num = 0;
char op = '+';
vector<int> nums;
while (index < s.size()) {
if (s[index] == ' ') {
index++;
continue;
}
//我们不能在遇到')'时直接退出,而是要将最近的num压栈!
//if (s[index] == ')') break; //{//index++; break;};
if (s[index] == '(') { //进括号了,递归
index++;
num = calExpr(s, index);
} else if (isdigit(s[index])) { //这里发现有数字,就把整个一个数字算出来。
num = calNum(s, index);
}
//这里s[index]不可能是数字了,因为上面的calNum()已经更新了index。
switch(op) { //here s[index] is '+-*/' or ')'
case '+':
nums.push_back(num);
break;
case '-':
nums.push_back(-num);
break;
case '*':
nums.back() *= num;
break;
case '/':
nums.back() /= num;
break;
default: //default要不要都可以
break;
}
op = s[index];
index++; //一定要记得把index++放到break前面!
if (op == ')') break;
}
int res = 0;
for (auto n : nums) {
res += n;
}
return res;
}
};
解法2:先转换成后序遍历的逆波兰表达式(RPN),然后再evaluate RPN。
class Solution {
public:
/**
* @param s: the given expression
* @return: the result of expression
*/
int calculate(string &s) {
int len = s.size();
vector<string> tokens;
//tokenize
int pos = 0, orig_pos = 0;
while (pos < len) {
while (pos < len && s[pos] == ' ') pos++;
if (pos == len) break;
orig_pos = pos;
if (!isdigit(s[pos])) {
tokens.push_back(s.substr(pos, 1));
pos++;
}
else {
while(pos < len && isdigit(s[pos])) pos++;
tokens.push_back(s.substr(orig_pos, pos - orig_pos));
}
}
#if 0
int leftPos = 0, rightPos = 0;
while(leftPos <= rightPos && rightPos < len) {
while(s[leftPos] == ' ') leftPos++;
rightPos = max(leftPos, rightPos);
if (!isdigit(s[rightPos])) {
tokens.push_back(s.substr(rightPos, 1));
rightPos++;
} else {
while(isdigit(s[rightPos])) rightPos++;
tokens.push_back(s.substr(leftPos, rightPos - leftPos + 1));
}
}
#endif
//generate RPN
len = tokens.size();
stack<string> optrStk;
vector<string> RPN;
map<string, int> prio;
prio["+"] = 1;
prio["-"] = 1;
prio["*"] = 2;
prio["/"] = 2;
for (int i = 0; i < len; i++) {
string token = tokens[i];
// cout << " i = " << i << " " << token << endl;
if (token.size() > 1 || (token[0] >= '0' && token[0] <= '9')) {
RPN.push_back(token);
continue;
}
if (token == "(") {
optrStk.push(token);
continue;
}
if (token == ")") {
while (!optrStk.empty() && optrStk.top() != "(") {
RPN.push_back(optrStk.top());
optrStk.pop();
}
optrStk.pop(); //pop "("
continue;
}
while (!optrStk.empty() && prio[optrStk.top()] >= prio[token]) {
RPN.push_back(optrStk.top());
optrStk.pop();
}
optrStk.push(token);
}
//dump the rest in optrStr to RPN
while (!optrStk.empty()) {
RPN.push_back(optrStk.top());
optrStk.pop();
}
//process RPN
stack<long long> RPNStk;
len = RPN.size();
if (len == 0) return 0;
if (len == 1) return stoi(RPN[0]);
for (int i = 0; i < len; i++) {
string token = RPN[i];
if (token.size() > 1 || (token[0] >= '0' && token[0] <= '9')) {
RPNStk.push(stoll(token));
continue;
} else {
long long top1 = RPNStk.top(); RPNStk.pop();
long long top2 = RPNStk.top(); RPNStk.pop();
long long result = 0;
switch (RPN[i][0]) {
case '*': result = top2 * top1; break;
case '/': result = top2 / top1; break;
case '+': result = top2 + top1; break;
case '-': result = top2 - top1; break;
default: break;
}
RPNStk.push(result);
}
}
return (int)RPNStk.top();
}
};
解法3:分治法。每次找到优先级最低的运算符,然后左右两边分别递归。
注意:
- 有括号的时候,我们必须用一个临时变量tmp。进入’(‘,tmp+=100,离开’)‘, tmp-=100。
不能直接把curPrio +=100 或-=100,不然优先级最低的运算符肯定就是’)‘了。 - 考虑(3+(2*(7-5))),这里运算符最低的是’+’号,将其分为"(3"和“(2*(7-5)))“。然后分别递归。
处理"(3"时,因为没有"+ - * /“,minPrioPos = -1,直接evaluate “3”,得到3。
处理(2 * (7-5)))“时候,运算符最低的时’*'号,将其分为”(2" 和 “(7-5)))”。同样,左边得到2,右边最低优先级是’-',又分为"(7"和"5)))",分别得到7和5。
class Solution {
public:
/**
* @param s: the given expression
* @return: the result of expression
*/
int calculate(string &s) {
return calExpr(s, 0, s.size() - 1);
}
private:
int calExpr(string &s, int left, int right) {
int tmp = 0, minPrio = INT_MAX / 3, minPrioPos = -1;
for (int i = left; i <= right; i++) {
int curPrio = INT_MAX; //数字和空格优先级无穷大
switch (s[i]) {
case '(': tmp += 100; break;
case ')': tmp -= 100; break;
case '+':
case '-': curPrio = 1 + tmp; break;
case '*':
case '/': curPrio = 2 + tmp; break;
default: break;
}
if (curPrio <= minPrio) {
minPrio = curPrio;
minPrioPos = i;
}
}
int res = 0;
//没有运算符和括号
if (minPrioPos == -1) {
for (int i = left; i <= right; i++) {
if (!isdigit(s[i])) continue;
res = res * 10 + (s[i] - '0');
}
return res;
}
//左右递归
int leftRes = 0, rightRes = 0;
leftRes = calExpr(s, left, minPrioPos - 1);
rightRes = calExpr(s, minPrioPos + 1, right);
switch (s[minPrioPos]) {
case '+': res = leftRes + rightRes; break;
case '-': res = leftRes - rightRes; break;
case '*': res = leftRes * rightRes; break;
case '/': res = leftRes / rightRes; break;
default: break;
}
return res;
}
};