Evaluate reverse Polish notation

Push numbers; on an operator, pop two and push the result. Two details decide whether it is correct: the first value popped is the right-hand operand, which only matters for - and /; and the division truncates toward zero, which is int(a / b) and not a // b.

Overview

The question, and what it is testing

Push numbers; on an operator, pop two and push the result. Two details decide whether it is correct: the first value popped is the right-hand operand, which only matters for - and /; and the division truncates toward zero, which is int(a / b) and not a // b.

Stacks & queuesCoding problemMedium

Step through it

What to watch

  • Numbers accumulate; an operator is the only thing that shrinks the stack.
  • 13 / 5 becomes 2, not 2.6 — integer division, truncated.
  • Exactly one value remains, and that is the result.

Say this out loud

"A stack. Numbers get pushed; an operator pops two operands, applies itself and pushes the result. The order matters - the first pop is the right operand - and for division I use int(a / b) rather than //, because Python floors and postfix expects truncation toward zero. At the end the stack holds exactly one value."

Evaluate reverse Polish notation

Evaluate an expression in postfix notation, for example ['4','13','5','/','+'].

Run it

The evaluator, on two standard expressions.

1Python
Output

Pop order, with the case that hides the bug printed beside the case that reveals it.

2Python
Output

The division that differs on negatives, and the two ways an expression can be malformed.

3Python
Output

Why postfix needs no parentheses

In infix, 4 + 13 / 5 is ambiguous without precedence rules, and (4 + 13) / 5 needs brackets to say the other thing. Postfix encodes the order in the token sequence itself, so there is nothing left to disambiguate — and that is why it evaluates with a stack and no parser.

The rule is mechanical. A number has no dependencies, so it waits on the stack. An operator's operands are always the most recent unconsumed values, which is exactly what "top of the stack" means.

The two lines that are wrong in most first attempts

Pop order. b = pop(); a = pop() and then a - b. Getting it backwards gives the right answer for + and * and the negated or reciprocal answer for - and / — which is why the bug survives the first test case anybody tries.

Division. Python's // floors, so -7 // 2 is -4. Postfix, like C and like every version of this problem, truncates toward zero and wants -3. int(a / b) gives that; math.trunc(a / b) says it more explicitly. For positive operands the two agree, so this is another bug that only appears on the test case with a negative number in it.

What the stack depth tells you

The depth is a validity check you get for free. Every number adds one; every binary operator removes one net. So a well-formed expression of n numbers and n−1 operators ends at depth 1.

Two failure modes follow, and an interviewer may ask for both. Popping from an empty stack means an operator with too few operands — malformed. Finishing with more than one value means numbers that nothing consumed, which is also malformed. Checking both is three lines and is the difference between a solution and a robust one.

The follow-up: converting from infix

"How would you get postfix in the first place?" The shunting-yard algorithm, and it is the same structure twice: one stack for operators, one output list. Numbers go straight to the output; an operator pops every operator of higher or equal precedence before pushing itself; brackets push and pop. Worth being able to name even if you are not asked to write it — it is the reason postfix exists as an intermediate form.

What to say out loud

A stack. Numbers get pushed; an operator pops two operands, applies itself and pushes the result. The order matters - the first pop is the right operand - and for division I use int(a / b) rather than //, because Python floors and postfix expects truncation toward zero. At the end the stack holds exactly one value.

Edge cases to raise

Volunteering these is most of what separates a correct answer from a good one.

Division by zero. int(a / b) raises ZeroDivisionError; decide whether that propagates or becomes a domain error of your own, and say so rather than discovering it.

Multi-digit and negative literals. int(t) handles both, but a tokeniser that splits on characters rather than whitespace will turn -4 into an operator and a number.

A single number is a valid expression. ["42"] should return 42, and a loop that requires at least one operator will not.

The follow-ups interviewers ask

"How would you produce the postfix in the first place?" The shunting-yard algorithm: one stack for operators, one output list. Numbers go straight out; an operator pops every operator of higher or equal precedence before pushing itself; a closing bracket pops until the opener. Worth naming even if you are not asked to write it.

"What about unary minus?" It breaks the pop-two rule, so it needs either a distinct token (~ or neg) or arity tracked per operator. Raising this unprompted is a good sign - it is the first thing that stops the evaluator being four lines.

"Evaluate prefix notation instead." Same stack, scanned right to left - and the pop order reverses, so the first pop becomes the left operand. It is a two-character change and it catches people who memorised the code rather than the reason.

Common wrong answers

"a = pop(); b = pop() then a - b." The swap. It gives the right answer for + and *, so the first test case passes and subtraction is negated.

"Use a // b for integer division." Python floors, postfix truncates. They agree on positive operands and differ by one on negatives, so this ships.

"Use eval()." It does not evaluate postfix at all, and offering it signals you have not read the question. Mentioning that you would never eval untrusted input is fine; offering it as the solution is not.

Recap in one screen

  • Numbers accumulate; an operator is the only thing that shrinks the stack.
  • 13 / 5 becomes 2, not 2.6 - integer division, truncated.
  • Exactly one value remains, and that is the result.
  • Worth trying: Add % and **. The modulo has the same sign disagreement as division, and exponentiation is the first operator here that is not commutative or associative.
  • Worth trying: Feed it ["2", "0", "/"]. Decide whether the right answer is an exception or a sentinel, and say which before you write it.

How the code works

The evaluator, the two orderings that separate right from wrong, and the division that differs on negative operands.

How the code works

  1. b = stack.pop(); a = stack.pop()The order is the answer to half this question. b came off first, so it is the right-hand operand of a - b.
  2. int(a / b)Truncation toward zero. a // b floors, which differs for exactly the cases where one operand is negative — and agrees everywhere else, which is what makes it a late-arriving bug.
  3. if len(stack) != 1: raiseThe depth is a free validity check. One value means well-formed; more means numbers nothing consumed.
  4. evaluate_swapped(["3","4","+"])Printed beside the correct version to make the point that + and * cannot detect the swap. Test with subtraction or you will not find it.

Change one thing

  • Add % and **. The modulo has the same sign disagreement as division, and exponentiation is the first operator here that is not commutative or associative.
  • Feed it ["2", "0", "/"]. Decide whether the right answer is an exception or a sentinel, and say which before you write it.

Where this runs

Real CPython, compiled to WebAssembly and running on your own machine — nothing is uploaded. The first run takes a few seconds while the interpreter downloads; after that it is immediate. Need more room, or want to paste your own attempt? Use the Python compiler.

Check yourself

0 of 4

Answer without scrolling back up.

  1. In a - b, which value was popped first?

  2. Why int(a / b) rather than a // b?

  3. What does the stack depth at the end tell you?

  4. Why does postfix need no parentheses?

Cheat sheet

Evaluate reverse Polish notation

Push numbers; on an operator, pop two and push the result. Two details decide whether it is correct: the first value popped is the right-hand operand, which only matters for - and /; and the division truncates toward zero, which is int(a / b) and not a // b.

INTERVIEW · vizlearn.in/interview/evaluate-reverse-polish-notation.html

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.