Start learning free

LESSON 397 OF 540 · STACK

Evaluate Reverse Polish Notation in Scala: O(n) with a Stack

Practice O(n) Read the full explanation

In reverse Polish notation, also called postfix notation, an operator comes after its two operands: 3 + 4 is written 3 4 +, and (1 + 2) * 3 is written 1 2 + 3 *. It needs no parentheses: the order of the tokens alone says what is computed first.

Given an array of strings tokens that holds a valid expression in reverse Polish notation, return the value of the expression.

  • Each token is either an operator, "+", "-", "*" or "/", or an integer such as "42" or "-7".
  • An operator works on the two values that come right before it, once those are worked out: the first of them is its left operand and the second its right operand. 8 3 - is 8 - 3 = 5, not 3 - 8.
  • The division of two integers truncates toward zero: it drops the fraction, so 7 / 2 is 3 and -7 / 2 is -3 (not -4).

 

Example 1:

Input: tokens = ["2", "1", "+", "3", "*"]
Output: 9
Explanation: ((2 + 1) * 3) = 9

Example 2:

Input: tokens = ["4", "13", "5", "/", "+"]
Output: 6
Explanation: (4 + (13 / 5)) = 4 + 2 = 6, because 13 / 5 is 2.6, truncated to 2.

Example 3:

Input: tokens = ["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"]
Output: 22
Explanation:
  ((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 22
6 / -132 is about -0.045, and truncating it toward zero gives 0.

 

Constraints:

  • 1 <= tokens.length <= 105
  • tokens[i] is "+", "-", "*", "/", or an integer.
  • Every integer in tokens, and every value computed along the way, fits in a 32-bit signed integer: from -231 to 231 - 1.
  • tokens is a valid expression in reverse Polish notation, and no division is by zero.

Note:

Your algorithm should run in O(n) time and use O(n) extra space, where n is the number of tokens.

STEP 1 OF 11
    Ctrl + Enter to check

    SCALA · STACK

    Evaluate Reverse Polish Notation in Scala, explained

    Problem overview

    In reverse Polish notation (postfix), an operator comes after its two operands: 3 + 4 is written 3 4 +, and (1 + 2) * 3 is written 1 2 + 3 *. No parentheses are needed, because the order of the tokens says what is computed first.

    The method evalRPN receives tokens, an Array[String] holding a valid expression, and returns its value. A token is an operator ("+", "-", "*" or "/") or an integer such as "42" or "-7". An operator works on the two values right before it: the first is its left operand and the second its right operand, so 8 3 - is 8 - 3 = 5. Division truncates toward zero, so 7 / 2 is 3 and -7 / 2 is -3.

    For ["2", "1", "+", "3", "*"] the answer is 9: first 2 + 1 = 3, then 3 * 3 = 9.

    Approach: a stack of values

    Read the tokens from left to right. A number can't be used until an operator arrives, so it waits. When an operator arrives, its operands are the two most recent values that haven't been used yet, which are exactly the two values on top of a stack. Pop them, apply the operator, and push the result, which now waits for the next operator.

    Each token is read once and each push or pop costs O(1), so the whole evaluation takes O(n) time and O(n) space for the stack.

    A valid expression turns every operator's two values into one, so at the end exactly one value remains on the stack: the result.

    Step by step

    1. Create the stack: val stack = scala.collection.mutable.Stack[Int](). It holds the values worked out so far, the most recent on top.
    2. Loop over the tokens with for (token <- tokens), left to right.
    3. Detect an operator by comparing token with the four one-character strings, joined with ||. A negative number such as "-7" starts with a minus sign but isn't equal to "-", so it is not mistaken for an operator.
    4. Pop the right operand first into b: it was worked out last, so it is on top.
    5. Pop the left operand into a. Popping b before a keeps the order the expression means: 8 3 - is a - b.
    6. Apply + by pushing a + b.
    7. Apply - by pushing a - b. The order matters here.
    8. Apply * by pushing a * b.
    9. Apply / in the final else by pushing a / b. Integer division in Scala truncates toward zero, as the lesson requires.
    10. Handle numbers in the outer else: push token.toInt.
    11. Return the result by popping the one value left on the stack.

    The solution in Scala

    class Solution {
        def evalRPN(tokens: Array[String]): Int = {
            val stack = scala.collection.mutable.Stack[Int]()
            for (token <- tokens) {
                if (token == "+" || token == "-" || token == "*" || token == "/") {
                    val b = stack.pop()
                    val a = stack.pop()
                    if (token == "+") {
                        stack.push(a + b)
                    } else if (token == "-") {
                        stack.push(a - b)
                    } else if (token == "*") {
                        stack.push(a * b)
                    } else {
                        stack.push(a / b)
                    }
                } else {
                    stack.push(token.toInt)
                }
            }
            return stack.pop()
        }
    }
    

    The Scala details worth a closer look:

    • == compares strings by value. token == "+" is true whenever the text is a plus sign. This is unlike Java's == on objects, which compares references.
    • Division truncates toward zero. -7 / 2 is -3, and 7 / -2 is also -3. The remainder keeps the sign of the left operand: -7 % 2 is -1. Floor division would give -4 (Math.floorDiv(-7, 2)), which is not what the lesson wants, so the plain / is right.
    • toInt accepts a leading minus. "-7".toInt is -7. A string that isn't a number, such as "+", throws a NumberFormatException, which is why operators are checked first.
    • pop() and push work at the top. The mutable Stack removes and returns the most recently pushed element with pop(). Popping an empty stack throws an exception, which a valid expression never causes.
    • Int overflow wraps silently in Scala: Int.MaxValue * 2 is -2. The constraints promise that every value fits in 32 bits, so Int is enough.
    • return stack.pop() could also be written without return, as the method's last expression.
    • The class name. It's written in full, scala.collection.mutable.Stack, so the code needs no import. Stack is not available without the package name or an import.

    Complexity

    Time: O(n). Every token is read once, and each token costs a constant number of stack operations and one arithmetic step or one toInt.

    Space: O(n). In the worst case, such as 1 2 3 4 + + +, the stack holds most of the numbers before the operators arrive.

    Edge cases and common mistakes

    • Operand order. ["8", "3", "-"] gives 5. Swapping a and b gives -5. The same applies to /.
    • Negative numbers. ["3", "-4", "*"] gives -12. The token "-4" goes to toInt, not to the operator branch.
    • Rounding of division. ["7", "2", "/"] gives 3 and ["-7", "2", "/"] gives -3. In the long example, 6 / -132 is about -0.045 and truncates to 0, so the product becomes 0 and the final answer is 22.
    • A single number. ["42"] gives 42, and ["-7"] gives -7: the number is pushed and then popped as the result.
    • Nested expressions. ["1", "2", "3", "*", "+"] gives 7: the * uses 2 and 3 first, and the + uses 1 and their product.
    • Testing for operators with startsWith("-"). That treats every negative number as a subtraction. Compare with the whole token.

    Testing

    • ["2", "1", "+", "3", "*"] gives 9.
    • ["4", "13", "5", "/", "+"] gives 6.
    • ["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"] gives 22.
    • ["8", "3", "-"] gives 5.
    • ["-7", "2", "/"] gives -3.
    • ["42"] gives 42.
    Keep going with Scala500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free