Stack
Intermediate 11 min read

Evaluating expressions

In one line

Stacks turn arithmetic text into answers: postfix (RPN) needs one stack of numbers, and normal infix expressions need either a sign-aware number stack or two stacks (numbers and operators) to respect precedence and parentheses.

What it is

In infix notation, the form people write, operators sit between numbers: 3 + 2 * 2. To evaluate it correctly you must follow precedence (* and / before + and -), left-to-right order for equal precedence, and parentheses.

In postfix notation (Reverse Polish Notation, RPN) the operator comes after its operands: 3 2 2 * +. Postfix needs no parentheses or precedence rules: you push numbers, and each operator pops two numbers and pushes the result. Dijkstra's shunting-yard algorithm converts infix to postfix with an operator stack, and calculators, compilers and interpreters use these ideas.

Why interviewers ask

Evaluate Reverse Polish Notation and the Basic Calculator problems (I, II and III) are common interview questions. They test careful parsing (multi-digit numbers, spaces, unary minus), operand order for - and /, integer division rules, and the ability to structure a solution around a stack rather than piling up special cases.

How it works

  1. 1Postfix: scan tokens. A number is pushed. An operator pops b (top) then a, computes a op b and pushes the result. At the end the stack holds exactly one value.
  2. 2Infix without parentheses (Basic Calculator II): read digits into num. When you reach an operator or the end, apply the previous operator: '+' pushes num, '-' pushes -num, '*' and '/' pop the top, combine it with num and push. The answer is the sum of the stack. Multiplication and division are applied immediately, additions are deferred, which is how precedence is respected.
  3. 3Infix with parentheses and + and - only (Basic Calculator I): keep a running result and sign; on '(' push the current result and sign and reset; on ')' pop them and combine.
  4. 4General infix (shunting-yard): use a number stack and an operator stack. Before pushing an operator, apply operators on the stack with higher or equal precedence (for left-associative operators). '(' is pushed; ')' applies operators until the matching '('.
  5. 5Integer division in these problems usually truncates toward zero. In Python, -7 // 2 is -4 (floor), so use int(a / b) or handle signs explicitly.

Worked example · Evaluating '3+2*2' and the RPN tokens ['2', '1', '+', '3', '*']

  1. '3+2*2': read 3. At '+', previous op is '+' (the start), push 3. op = '+'. Stack [3].
  2. Read 2. At '*', previous op is '+', push 2. op = '*'. Stack [3, 2].
  3. Read 2, end of string. Previous op is '*': pop 2, push 2 * 2 = 4. Stack [3, 4]. Sum = 7, not 10.
  4. RPN: push 2, push 1. '+': pop b = 1, a = 2, push 3. Push 3. '*': pop b = 3, a = 3, push 9. Result 9, which is (2 + 1) * 3.

Picture it

Postfix is like a recipe written in the order you actually cook: get flour, get eggs, mix. Infix is like a recipe that says 'mix (flour and eggs)', and the stack is the counter where you put ingredients down until the instruction that uses them arrives.

Reference code

def eval_rpn(tokens):
    stack = []
    for tok in tokens:
        if tok in {"+", "-", "*", "/"}:
            b, a = stack.pop(), stack.pop()      # order matters for - and /
            if tok == "+": stack.append(a + b)
            elif tok == "-": stack.append(a - b)
            elif tok == "*": stack.append(a * b)
            else: stack.append(int(a / b))       # truncate toward zero
        else:
            stack.append(int(tok))
    return stack[0]


def calculate(s):
    """+, -, *, / on non-negative integers, no parentheses."""
    stack, num, op = [], 0, "+"
    for i, ch in enumerate(s):
        if ch.isdigit():
            num = num * 10 + int(ch)
        if ch in "+-*/" or i == len(s) - 1:
            if op == "+": stack.append(num)
            elif op == "-": stack.append(-num)
            elif op == "*": stack.append(stack.pop() * num)
            else: stack.append(int(stack.pop() / num))
            op, num = ch, 0
    return sum(stack)

eval_rpn is O(n) time and space; note b is popped before a. calculate handles + - * / with spaces and multi-digit numbers in O(n): spaces are skipped because they are neither digits nor operators, and the last character always triggers the final operation.

Key patterns

Evaluate postfix

When: Tokens already in RPN

How: Number stack; operator pops b then a and pushes a op b.

Defer + and -, apply * and / immediately

When: Infix with + - * / and no parentheses

How: Push signed numbers; for * and / pop, combine, push; answer is the sum.

Save context on '('

When: Infix with + - and parentheses

How: Push (result, sign) on '(' and restore on ')'; or recurse on the inner expression.

Shunting-yard

When: Full precedence, parentheses, or converting to postfix

How: Operator stack; pop operators with higher or equal precedence before pushing a new one; ')' pops until '('.

Decode nested strings

When: Decode '3[a2[c]]' to 'accaccacc'

How: Stack of (previous string, repeat count); on ']' pop and repeat the current string.

Real-world use

Calculators

HP's scientific and financial calculators famously use RPN input, and ordinary calculators parse infix with precedence rules using stack-based algorithms like shunting-yard.

Spreadsheets

Excel and Google Sheets parse formulas like =A1+B1*2 into an expression structure that respects precedence and parentheses before evaluating them across cells.

Stack-based virtual machines

The JVM, CPython and WebAssembly execute bytecode on an operand stack: a + b * c compiles to push a, push b, push c, multiply, add, which is postfix order.

PostScript and Forth

PostScript, the page-description language used by many printers, and the Forth language are written directly in postfix, so the program text is evaluated with a stack.

In a project

A pricing team wants admins to write discount rules like 'base * 0.9 - (shipping + 5)' in a dashboard, safely, without running arbitrary code with eval().

  1. Tokenise the rule into numbers, variable names, operators and parentheses, rejecting anything else.
  2. Convert to postfix with shunting-yard, checking parentheses balance and reporting errors with positions.
  3. Store the postfix form; at checkout, substitute variables and evaluate with one number stack.
  4. Add guards: division by zero returns an error, results are rounded to cents, and rules are unit-tested with sample carts.

Impact: Admins change pricing rules without a deploy, and there is no code-injection risk because only whitelisted tokens are evaluated. Evaluation is O(n) in the rule length, so it adds microseconds at checkout.

Trade-offs

Use it when

  • You need to evaluate arithmetic from text safely, without eval()
  • Precedence and nesting must be handled in one pass
  • You want to compile an expression once (to postfix) and evaluate it many times

Avoid it when

  • The language is complex (functions, variables, types): use a proper parser generator or a recursive descent parser
  • Floating-point money calculations: use decimal types to avoid rounding errors
  • The expression is trusted Python code you control anyway (then the language already evaluates it, but never eval untrusted input)

Cost: Time O(n) in the number of characters or tokens. Space O(n) for the stacks in the worst case (deep nesting or many pending additions).

Common mistakes

  • Popping operands in the wrong order: for a - b and a / b, the first pop is b.
  • Using Python's // for division that should truncate toward zero: -7 // 2 is -4, but the expected answer is -3.
  • Reading only single digits, so '12+3' is treated as 1, 2, +, 3.
  • Forgetting to apply the last pending operation when the string ends (or ends with a space).
  • Ignoring unary minus, such as '-(2+3)' or '1-(-2)', in calculators with parentheses.

Interview questions

Quick revision

  • Postfix: numbers push; operator pops b, a and pushes a op b.
  • Infix without parens: push +n / -n, apply * and / immediately, sum the stack.
  • Parens with + and -: push (result, sign) on '(', restore on ')'.
  • Shunting-yard: operator stack, pop higher or equal precedence first.
  • Truncate toward zero: int(a / b), not a // b, for negatives.
  • Build multi-digit numbers: num = num * 10 + digit.
  • Never eval() user input.

Check yourself

1. What is the value of the RPN expression ['4', '13', '5', '/', '+']?

2. In RPN, after reading '7 2 -', what is pushed?

3. Why does Basic Calculator II apply * and / immediately but push + and - operands?

4. What does Python's -7 // 2 evaluate to?

Saved on this device. Create a free account to keep it on every device.

Practise this with Sam

A real mock interview, out loud, with an honest scorecard. Free to start.

Start free