Problem
Given a string s
representing a valid expression, implement a basic calculator to evaluate it, and return the result of the evaluation.
Note: You are not allowed to use any built-in function which evaluates strings as mathematical expressions, such as eval()
.
Examples
Example 1:
Input:
s = "3+2*2"
Output:
7
Example 2:
Input:
s = " 3/2 "
Output:
1
Example 3:
Input:
s = " 3+5 / 2 "
Output:
5
Constraints
1 <= s.length <= 3 * 10^5
s
consists of integers and operators('+', '-', '*', '/')
separated by some number of spaces.s
represents a valid expression.- All the integers in the expression are non-negative integers in the range
[0, 2^31 - 1]
. - The answer is guaranteed to fit in a 32-bit integer.
Solution
Method 1 - Using Stack for Operands 🥈
If all operators had the same priority, we could sequentially calculate each number and operator, saving only the latest result. However, to account for operator priorities (’*
’ and ‘/
’ having higher priority than ‘+’ and ‘-’), we utilise a stack to track encountered numbers.
A straightforward algorithm is:
- Use global variables:
prevOpr
to remember the last operator encountered,num
for the current number, and a stack to hold numbers. - Traverse the string character by character following these rules:
- If the current character is a blank, simply move to the next step.
- If it’s a digit, build the number using a while loop to capture the full digit sequence.
- Otherwise, it’s an operator, indicating completion of a new number (or the end of the string). Process the number with
prevOpr
based on these rules:- If
prevOpr
is ‘+’ or ‘-’, we may encounter operator with higher priority in the future, so appendnum
or-num
to the stack:case '+': stack.push(num); break; case '-': stack.push(-num); break;
- If `prevOpr` is '`*`' or '`/`', execute the operation immediately, pop the last number from the stack, apply the operation, and push the result back to the stack: ```java case '*': stack.push(stack.pop() * num); break; case '/': stack.push(stack.pop() / num); break; ``` - At last, we have a stack containing a couple of numbers and all of them have the same operator '+'. Finally, sum up all numbers in the stack and return the result:: `while(!stack.isEmpty())res += stack.pop();`
- If
Initialise prevOpr
to ‘+’ for the first number:
char prevOpr = '+';
Add an extra ‘+’ to the end of the string to ensure the last number is processed. (as when the last number is added, the prevOpr
still needs to execute on it):
s = s + "+"; // kind of hacky but needed
Code
Java
public int calculate(String s) {
if(s==null || s.length()==0) return 0;
Stack<Integer> stack = new Stack<Integer>();
int num = 0;
char prevOpr = '+';
s = s + "+";
for(int i=0;i<s.length();i++){
char ch = s.charAt(i);
if (ch == ' ') {
continue;
}
else if(Character.isDigit(ch)) {
num = ch - '0';
while(i+1 < s.length() && Character.isDigit(s.charAt(i+1))) {
num = num * 10 + s.charAt(i+1) - '0';
i++;
}
}else {
// as string is valid, we are here that means operator
switch(prevOpr) {
case '+':
stack.push(num);
break;
case '-':
stack.push(-num);
break;
case '*':
stack.push(stack.pop() * num);
break;
case '/':
stack.push(stack.pop() / num);
break;
default:
return -1;
}
num = 0;
prevOpr = ch;
}
}
int ans = 0;
for(int stackNum:stack){
ans += stackNum;
}
return ans;
}
Method 2 - Without Using Stack 🏆
A further insight is that we can always sum all stack numbers except the latest one without affecting the result. Thus, we only need two variables instead of a stack:
prevNum
for the sum of all numbers except the latest one (to be added to the final result after the loop).num
for the latest number being processed.
Code
Java
public int calculate(String s) {
if(s==null || s.length()==0) return 0;
int num = 0, prevNum = 0, ans = 0;
char prevOpr = '+';
s = s + "+";
for(int i=0;i<s.length();i++){
char ch = s.charAt(i);
if (ch == ' ') continue;
else if(Character.isDigit(ch)) {
num = ch - '0';
while(i+1 < s.length() && Character.isDigit(s.charAt(i+1))) {
num = num * 10 + s.charAt(i+1) - '0';
i++;
}
} else {
// as string is valid, we are here that means operator
switch(prevOpr) {
case '+':
ans += prevNum;
prevNum = num;
break;
case '-':
ans += prevNum;
prevNum = -num;
break;
case '*':
prevNum *= num;
break;
case '/':
prevNum /= num;
break;
default:
return -1;
}
num = 0;
prevOpr = ch;
}
}
ans += prevNum;
return ans;
}
Method 3 - Without Modifying Input String, i.e. without s+"+"
Code
Java
public int calculate2(String s) {
int result = 0;
int tempResult = 0;
int currentNum = 0;
char currentOp = '+';
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (Character.isDigit(c)) {
num = ch - '0';
while(i+1 < s.length() && Character.isDigit(s.charAt(i+1))) {
num = num * 10 + s.charAt(i+1) - '0';
i++;
}
}
if (i == s.length() - 1 || !Character.isDigit(c) && c != ' ') {
switch (currentOp) {
case '+':
result += tempResult;
tempResult = currentNum;
break;
case '-':
result += tempResult;
tempResult = -currentNum;
break;
case '*':
tempResult *= currentNum;
break;
case '/':
tempResult /= currentNum;
break;
}
currentOp = c;
currentNum = 0;
}
}
result += tempResult;
return result;
}