Basic Calculator II medium
Problem Statement
Evaluate a given arithmetic expression containing +, -, *, and / operators. The expression is given as a string, and you should follow the standard order of operations (PEMDAS/BODMAS), where multiplication and division have higher precedence than addition and subtraction.
Example 1
Input: "3+2*2"
Output: 7
Explanation: 3 + (2 * 2) = 7
Example 2
Input: " 3/2 "
Output: 1
Explanation: 3 / 2 = 1
(integer division)
Steps
-
Handle Whitespace: Remove leading and trailing whitespace from the input string.
-
Split into Tokens: Split the string into tokens (numbers and operators). We'll use a
Stringtokenizer
for simplicity. Alternatively, you could iterate character by character. -
Two-Stack Approach: We'll use two stacks: one for numbers (
numStack
) and one for operators (opStack
). -
Iteration: Iterate through the tokens.
- If a token is a number, push it onto
numStack
. - If a token is an operator:
- While
opStack
is not empty and the precedence of the current operator is less than or equal to the precedence of the top operator inopStack
, perform the operation using the top two numbers fromnumStack
and push the result back ontonumStack
. Then, pop the operator fromopStack
. - Push the current operator onto
opStack
.
- While
- If a token is a number, push it onto
-
Final Calculations: After iterating through all tokens, perform any remaining operations in
opStack
. -
Result: The final result will be the top element of
numStack
.
Explanation
The two-stack approach mimics the order of operations. Higher precedence operators are processed first because they're pushed onto the opStack
earlier and will be processed before lower-precedence operators during the iterative evaluation. The use of a stack ensures that the operations are performed in the correct order - last in, first out (LIFO). Integer division is handled naturally in Java.
Code
import java.util.Stack;
import java.util.StringTokenizer;
class Solution {
public int calculate(String s) {
s = s.trim(); //remove leading/trailing whitespace
Stack<Integer> numStack = new Stack<>();
Stack<Character> opStack = new Stack<>();
StringTokenizer st = new StringTokenizer(s, "+-*/ ", true); //tokenize with spaces
while (st.hasMoreTokens()) {
String token = st.nextToken();
if (Character.isDigit(token.charAt(0))) {
numStack.push(Integer.parseInt(token));
} else if (token.equals("+") || token.equals("-") || token.equals("*") || token.equals("/")) {
while (!opStack.isEmpty() && precedence(token.charAt(0)) <= precedence(opStack.peek())) {
numStack.push(performOp(numStack.pop(), numStack.pop(), opStack.pop()));
}
opStack.push(token.charAt(0));
}
}
while (!opStack.isEmpty()) {
numStack.push(performOp(numStack.pop(), numStack.pop(), opStack.pop()));
}
return numStack.pop();
}
private int precedence(char op) {
if (op == '+' || op == '-') return 1;
return 2; // * or /
}
private int performOp(int b, int a, char op) {
switch (op) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b; //integer division
default: return 0;
}
}
}
Complexity
- Time Complexity: O(n), where n is the length of the input string. We iterate through the string once.
- Space Complexity: O(n) in the worst case, which occurs if the input string has only operators, resulting in n elements in the stacks. In most cases the space complexity would be much less than O(n).