227. Basic Calculator II

Intuition

The expression is in infix notation (e.g., "3+2*2"), which needs to respect operator precedence (* and / over + and -). Instead of evaluating directly, we simulate infix-to-postfix conversion and evaluate on the fly using a stack. This helps maintain order and precedence without using built-in eval().

  • Traverse the input string character by character.

  • Build multi-digit numbers.

  • Use a stack to store numbers and apply operators when precedence rules dictate.

  • After traversing the input, apply any remaining operations in the stack.

  • Return the final result from the stack.

Complexity

Space Complexity
Time Complexity

O(n)\text{O}(n)

O(n)\text{O}(n)

Code

// Determines the precedence level of the given operator
int getPrecedence(char operator) {
    if (operator == '*' || operator == '/') return 2;
    else if (operator == '+' || operator == '-') return 1;
    return -1;
}

// Converts infix expression to postfix on-the-fly and evaluates it using stacks
int evaluateInfixExpression(String expression) {
    Stack<Character> operatorStack = new Stack<>();
    Stack<Integer> valueStack = new Stack<>();

    for (int index = 0; index < expression.length(); ++index) {
        char currentChar = expression.charAt(index);

        // Skip whitespace
        if (currentChar == ' ') continue;

        // If it's a digit, parse the full number and evaluate
        if (Character.isDigit(currentChar)) {
            StringBuilder currentNumber = new StringBuilder();
            while (index < expression.length() && Character.isDigit(expression.charAt(index))) {
                currentNumber.append(expression.charAt(index));
                index++;
            }
            index--; // step back to compensate for extra index increment
            evaluateToken(valueStack, currentNumber.toString());
        } else {
            // While there is an operator on the stack with higher or equal precedence, apply it
            while (!operatorStack.isEmpty() && getPrecedence(currentChar) <= getPrecedence(operatorStack.peek())) {
                evaluateToken(valueStack, operatorStack.pop().toString());
            }

            // Push the current operator
            operatorStack.push(currentChar);
        }
    }

    // Apply remaining operators
    while (!operatorStack.isEmpty()) {
        evaluateToken(valueStack, operatorStack.pop().toString());
    }

    // Final result is at the top of the value stack
    return valueStack.peek();
}

// Applies an operation or pushes a number to the stack
void evaluateToken(Stack<Integer> valueStack, String token) {
    if (token.equals("*")) {
        int operand2 = valueStack.pop();
        int operand1 = valueStack.pop();
        valueStack.push(operand1 * operand2);
    } else if (token.equals("/")) {
        int operand2 = valueStack.pop();
        int operand1 = valueStack.pop();
        valueStack.push(operand1 / operand2); // Integer division truncates toward zero
    } else if (token.equals("+")) {
        int operand2 = valueStack.pop();
        int operand1 = valueStack.pop();
        valueStack.push(operand1 + operand2);
    } else if (token.equals("-")) {
        int operand2 = valueStack.pop();
        int operand1 = valueStack.pop();
        valueStack.push(operand1 - operand2);
    } else {
        // Token is a number; convert and push onto the value stack
        valueStack.push(Integer.parseInt(token));
    }
}

// Entry point method
public int calculate(String expression) {
    return evaluateInfixExpression(expression);
}

Last updated