Evaluate Reverse Polish Notation — Operand Stack Calculator
Advertisement
Problem Statement
Evaluate the value of an arithmetic expression in Reverse Polish Notation (RPN). Valid operators are +, -, *, and /. Each operand may be an integer or another expression. Division between two integers truncates toward zero.
Constraints:
1 <= tokens.length <= 10^4tokens[i]is either an operator+,-,*,/, or an integer in the range[-200, 200].- The given RPN expression is always valid.
Input: tokens = ["2","1","+","3","*"]
Output: 9
Explanation: ((2 + 1) * 3) = 9Input: tokens = ["4","13","5","/","+"]
Output: 6
Explanation: (4 + (13 / 5)) = (4 + 2) = 6Input: tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
Output: 22Why This Problem Matters
LC 150 is the canonical example of how stacks power expression evaluation — the same technique used by every compiler and calculator on the planet. Reverse Polish Notation eliminates the need for parentheses by using operand order to define precedence.
Companies that ask this problem: Amazon, LinkedIn, Microsoft, and Bloomberg. It is often a warm-up before harder expression problems like Basic Calculator I/II (LC 224, 227) and Decode String (LC 394).
Understanding RPN evaluation is essential for:
- Compiler design: abstract syntax trees and code generation.
- Calculator apps: many calculators use RPN internally.
- Stack machines: the JVM and Python's CPython both use stack-based bytecode execution.
- Expression parsing: converting infix to postfix and evaluating.
The Core Insight
What is RPN? In normal (infix) notation, operators go between operands: 2 + 3. In RPN (postfix), operators come after their operands: 2 3 +. The order of evaluation is always left-to-right, and no parentheses are needed.
Stack evaluation: Process tokens left to right:
- If the token is a number, push it onto the stack.
- If the token is an operator (
+,-,*,/), pop two operands from the stack (second-pop is the left operand, first-pop is the right operand), apply the operator, and push the result.
After processing all tokens, the stack contains exactly one element — the final result.
Why LIFO is perfect here: The last two operands pushed are the ones needed for the current operator. The LIFO property of a stack naturally gives us the most recently seen operands in the correct order.
Division truncation toward zero: In Python, // floors toward negative infinity, which differs from truncation toward zero for negative numbers. Use int(a / b) instead of a // b.
Visual Dry Run
Input: ["4","13","5","/","+"]
| Token | Action | Stack (bottom to top) |
|---|---|---|
| "4" | push 4 | [4] |
| "13" | push 13 | [4, 13] |
| "5" | push 5 | [4, 13, 5] |
| "/" | pop 5 (right), pop 13 (left): 13/5=2; push 2 | [4, 2] |
| "+" | pop 2 (right), pop 4 (left): 4+2=6; push 6 | [6] |
Result: 6 ✓
Input: ["2","1","+","3","*"]
| Token | Action | Stack |
|---|---|---|
| "2" | push 2 | [2] |
| "1" | push 1 | [2, 1] |
| "+" | pop 1, pop 2: 2+1=3; push 3 | [3] |
| "3" | push 3 | [3, 3] |
| "*" | pop 3, pop 3: 3*3=9; push 9 | [9] |
Result: 9 ✓
Solution (Optimal)
# Python — operand stack, O(n) time and space
def evalRPN(tokens: list[str]) -> int:
stack = []
operators = {'+', '-', '*', '/'}
for token in tokens:
if token in operators:
# Pop two operands: b is second-to-top (right), a is top (left)
# Wait — actually: pop order is LIFO, so first pop = right operand
b = stack.pop() # right operand (pushed second)
a = stack.pop() # left operand (pushed first)
if token == '+':
stack.append(a + b)
elif token == '-':
stack.append(a - b)
elif token == '*':
stack.append(a * b)
else: # '/'
# Truncate toward zero (not floor division)
stack.append(int(a / b))
else:
stack.append(int(token))
return stack[0]// JavaScript — operand stack, O(n) time and space
function evalRPN(tokens) {
const stack = [];
const operators = new Set(['+', '-', '*', '/']);
for (const token of tokens) {
if (operators.has(token)) {
const b = stack.pop(); // right operand
const a = stack.pop(); // left operand
switch (token) {
case '+': stack.push(a + b); break;
case '-': stack.push(a - b); break;
case '*': stack.push(a * b); break;
case '/':
// Truncate toward zero (same as C/Java integer division)
stack.push(Math.trunc(a / b));
break;
}
} else {
stack.push(parseInt(token, 10));
}
}
return stack[0];
}Complexity:
| Approach | Time | Space | Notes |
|---|---|---|---|
| Operand stack | O(n) | O(n) | Single pass; stack holds at most n/2 operands |
Common Mistakes
-
Popping operands in the wrong order. The first pop gives the right operand (pushed second); the second pop gives the left operand (pushed first).
a - bwherebis first-popped andais second-popped. Getting this backwards makes5 1 -evaluate to1 - 5 = -4instead of5 - 1 = 4. -
Using Python's
//for division. Python's//(floor division) truncates toward negative infinity:-7 // 2 = -4, but RPN truncates toward zero:-7 / 2 = -3. Useint(a / b)instead. -
Not converting string tokens to integers. Tokens arrive as strings. Forgetting
int(token)when pushing operands causes string concatenation instead of addition on+. -
Using
token not in operatorscheck by comparing to"+" or "-" or "*" or "/". Comparing strings withina set is O(1) and clean. Chaining==comparisons is verbose and error-prone. -
Assuming only single-digit numbers. Tokens can be negative (
"-3") or multi-digit ("100").int(token)handles both correctly, but trying to checktoken.isdigit()fails for negative numbers (the-sign makes it return False).
Interview Tips
- Explain the pop order: "The stack is LIFO. When we push operands in order
a b, then apply operatorop, we popbfirst (right operand) thena(left operand). Soa op b=second-pop op first-pop." - Address Python division: "Python's
//floors toward negative infinity, which differs from C/Java truncation toward zero for negative dividends. I useint(a / b)to match the expected behavior." - Connect to compiler design: "RPN is exactly the postfix bytecode that stack-based virtual machines like the JVM and CPython use. Every method call, arithmetic, and comparison compiles down to push/pop/apply operations."
Follow-up Questions
- Basic Calculator I (LC 224) — evaluate
+,-, with parentheses (no*,/). Requires precedence handling. - Basic Calculator II (LC 227) — evaluate
+,-,*,/without parentheses. Use a stack to handle*//before+/-. - Convert infix to RPN. The Shunting-yard algorithm uses a stack to handle operator precedence during conversion.
- What if the operator set includes
^(exponentiation)? Add a case in the switch; handle right-associativity of^if converting from infix. - Evaluate with variables. Maintain a variable map alongside the operand stack; push variable values when encountered.
Key Takeaways
- Operand stack pattern: push numbers, pop two operands when an operator is encountered, push the result. Final stack top is the answer.
- Pop order matters: first pop = right operand, second pop = left operand. This is the most common bug in this problem.
- Python division: always use
int(a / b)(nota // b) for RPN — the problem requires truncation toward zero, not floor division. - Detecting negative numbers in tokens:
int(token)handles both negative numbers ("-3") and multi-digit numbers ("100") — do not usetoken.isdigit()as the branch condition. - RPN is the postfix form that eliminates ambiguity and parentheses — the exact format used by stack-based virtual machines internally.
- This problem is a direct stepping stone to Basic Calculator I/II and expression parsing problems at FAANG.
Advertisement