Start learning free

LESSON 396 OF 539 · STACK

Evaluate Reverse Polish Notation in C#: A Stack in O(n)

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

    C# · STACK

    Evaluate Reverse Polish Notation in C#, explained

    Problem overview

    The method EvalRPN receives a string[] called tokens that holds a valid expression in reverse Polish notation (postfix), and returns its value as an int. In this notation 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 alone says what to compute first.

    Each token is one of the operators "+", "-", "*", "/", or an integer such as "42" or "-7". An operator works on the two values right before it, once those are worked out; 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.

    The tokens {"2","1","+","3","*"} give 9, because (2 + 1) * 3 = 9. The tokens {"4","13","5","/","+"} give 6, because 13 / 5 is 2 and 4 + 2 = 6.

    Approach: a stack of values worked out so far

    Read the tokens from left to right and keep a stack: a collection where the last item added is the first one removed. When a token is a number, push it. When it is an operator, its two operands are the two most recent values that haven't been used yet, and those are exactly the top two items on the stack. Pop them, apply the operator, and push the result, which takes the place of its operands for the next operator to use.

    Every operator turns two values into one, and every number adds one, so a valid expression ends with exactly one value on the stack: the answer.

    For {"4","13","5","/","+"}: push 4, push 13, push 5. The stack is now 4, 13, 5 with 5 on top. The / pops 5 and then 13 and pushes 13 / 5 = 2, so the stack is 4, 2. The + pops 2 and 4 and pushes 6. The last pop returns 6.

    Step by step

    1. Create the stack: a Stack<int> named stack. It holds the values so far, the most recent on top.
    2. Loop over the tokens with foreach (string token in tokens). Each token is read once, so the whole evaluation is linear.
    3. Test for an operator: compare token with "+", "-", "*" and "/", joined with ||. A negative number such as "-7" starts with a minus sign too, but it isn't equal to the one-character token "-", so it is treated as a number.
    4. Pop the right operand first: int b = stack.Pop(). It was worked out last, so it is on top.
    5. Pop the left operand second: int a = stack.Pop(). This order keeps the meaning: for 8 3 -, a is 8 and b is 3, so a - b is 5.
    6. Apply the operator with an if / else if chain and push the result: a + b, a - b, a * b, and, in the last else, a / b. For - and / the order matters.
    7. Otherwise it's a number: an else on the first if pushes int.Parse(token). It waits on the stack until an operator needs it.
    8. Return the answer: after the loop, stack.Pop() gives the single value left.

    The solution in C#

    class Solution {
        public int EvalRPN(string[] tokens) {
            Stack<int> stack = new Stack<int>();
            foreach (string token in tokens) {
                if (token == "+" || token == "-" || token == "*" || token == "/") {
                    int b = stack.Pop();
                    int 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(int.Parse(token));
                }
            }
            return stack.Pop();
        }
    }
    

    The C# details worth knowing:

    • Integer division truncates toward zero. In C#, -7 / 2 is -3, not -4, and 7 / -2 is also -3. That matches the lesson's rule with no extra code. For the sample 6 / -132, the result is 0.
    • String comparison with ==. In C#, == on two strings compares their contents, so token == "+" does what it looks like.
    • int.Parse and signs. int.Parse("-7") returns -7. A bare "+" is not a number: int.Parse("+") throws FormatException, which the code never reaches because operators are handled first.
    • Pop on an empty stack throws InvalidOperationException. For a valid expression that can't happen. For an invalid one such as {"+"}, it does.
    • int overflow wraps silently. a * b with values that don't fit in 32 bits would wrap. The lesson guarantees that every value computed along the way fits, so int is enough. One more rare case: int.MinValue / -1 throws OverflowException, because the true result doesn't fit.
    • Pop order. Stack<T>.Pop() removes and returns the top item, so the first Pop returns the most recent value: that is the right operand.

    Complexity

    Time: O(n). Each of the n tokens is read once, and each one does a constant number of stack operations, comparisons and a parse. Push and Pop take constant time.

    Space: O(n). In the worst case all the numbers come first and are on the stack at the same time, for example 1 2 3 4 + + +, where four values wait before the first operator. The stack never holds more than the number of tokens.

    Edge cases and common mistakes

    • A single number. {"3"} gives 3 and {"-7"} gives -7: the one value is pushed and popped.
    • Operand order. {"8","3","-"} gives 5. Popping a before b would compute 3 - 8 = -5.
    • Division of negatives. {"-7","2","/"} gives -3, {"7","-2","/"} gives -3 and {"7","2","/"} gives 3. Using Math.Floor on a floating-point division would give -4 for the first.
    • Negative numbers mistaken for operators. Testing token[0] == '-' would treat "-7" as the minus operator. Comparing the whole token avoids it.
    • Quotients that round to zero. {"6","-132","/"} gives 0, since 6 / -132 truncates toward zero.
    • Popping in the wrong order for /. 13 5 / would compute 5 / 13, which is 0, instead of 2.

    Testing

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