NEETCODE - Stack: Evaluate Reverse Polish Notation
1. Problem Statement
You are given an array of strings tokens that represents a valid arithmetic expression in Reverse Polish Notation (RPN).
Return the integer that represents the evaluation of the expression.
Rules
- The operands may be integers or the results of other operations
- The operators include
'+','-','*', and'/' - Assume that division between integers always truncates toward zero
Example 1
Input:
tokens = ["1", "2", "+", "3", "*", "4", "-"]
Output:
5
Explanation:
((1 + 2) * 3) - 4 = 5
Example 2
Input:
tokens = ["10", "6", "9", "3", "/", "-11", "*", "/", "*", "17", "+", "5", "+"]
Output:
22
Constraints
1 <= tokens.length <= 1000tokens[i]is"+", "-", "*", or "/", or a string representing an integer in the range[-100, 100]`
2. Your Solution Review & Feedback
What You Implemented
You wrote two implementations of this problem:
First attempt:
class Solution:
def __init__(self):
self.stack = []
def push(self, value):
self.stack.append(value)
def pop(self):
return self.stack.pop()
def is_number(self, character: str) -> bool:
try:
int(character)
return True
except:
return False
def evalRPN(self, tokens: List[str]) -> int:
for character in tokens:
if self.is_number(character):
character_int = int(character)
self.push(character_int)
else:
second_addend = self.pop()
first_addend = self.pop()
if character == "+":
self.push(first_addend + second_addend)
elif character == "-":
self.push(first_addend - second_addend)
elif character == "*":
self.push(first_addend * second_addend)
elif character == "/":
self.push(int(first_addend / second_addend))
return self.stack[0]
Second attempt:
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
stack = []
for token in tokens:
if token.strip("-").isdigit():
stack.append(int(token))
elif token == "+":
a = stack.pop()
b = stack.pop()
stack.append(a + b)
elif token == "-":
a = stack.pop()
b = stack.pop()
stack.append(b - a)
elif token == "*":
a = stack.pop()
b = stack.pop()
stack.append(a * b)
elif token == "/":
a = stack.pop()
b = stack.pop()
stack.append(int(b / a))
return stack.pop()
What Worked Well
✅ Core algorithm is perfect: Both solutions correctly use a stack to evaluate RPN expressions.
✅ Correct operand order in second solution: In your second attempt, you correctly handle b - a and b / a (not a - b or a / b). This is crucial because subtraction and division are not commutative.
✅ Good progression: You showed improvement from first to second attempt, making the code cleaner.
✅ Handles negative numbers: Your second solution uses strip("-").isdigit() to handle negative numbers correctly.
Issues Identified
❌ Incorrect operand order in first solution:
When you pop from a stack, the order matters for non-commutative operations!
Overcomplicated number detection in first solution:
This works, but using bare except is bad practice. Should catch specific exception.
Your second solution uses a better approach: tokens[i].strip("-").isdigit().
Learning Points
Order matters for non-commutative operations: When popping two values from a stack for subtraction or division, remember that the first popped value is the second operand in the original expression.
Local variables > instance variables for stateless functions: Your second solution is better because it doesn't maintain state between calls.
Clear variable names matter:
first_addendis confusing when it's actually the value that was pushed second. Generic names likea/borright/leftare clearer.Python division quirks:
int(a / b)truncates toward zero (correct for this problem).
Recommended Improvements
- Iterate directly over tokens instead of using indices:
for token in tokens:
if token.strip("-").isdigit():
- Consider using a dictionary for operators to avoid the long
elifchain:
ops = {
"+": lambda a, b: b + a,
"-": lambda a, b: b - a,
"*": lambda a, b: b * a,
"/": lambda a, b: int(b / a),
}
if token in ops:
a = stack.pop()
b = stack.pop()
stack.append(ops[token](a, b))
Approach 1: Recursion (Not Optimal)
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
def evaluate(index):
token = tokens[index]
if token in "+-*/":
right, right_index = evaluate(index - 1)
left, left_index = evaluate(right_index - 1)
if token == "+":
result = left + right
elif token == "-":
result = left - right
elif token == "*":
result = left * right
elif token == "/":
result = int(left / right)
return result, left_index
else:
return int(token), index - 1
result, _ = evaluate(len(tokens) - 1)
return result
Time Complexity: O(n) - visit each token once
Space Complexity: O(n) - recursion call stack depth
Why this doesn't work well:
- Overly complex for this problem
- Hard to understand and debug
Approach 2: Stack (Optimal)
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
operand_stack = []
for current_token in tokens:
if current_token in ["+", "-", "*", "/"]:
right_operand = operand_stack.pop()
left_operand = operand_stack.pop()
if current_token == "+":
result = left_operand + right_operand
elif current_token == "-":
result = left_operand - right_operand
elif current_token == "*":
result = left_operand * right_operand
elif current_token == "/":
result = int(left_operand / right_operand)
operand_stack.append(result)
else:
operand_stack.append(int(current_token))
return operand_stack[0]
Key insight: RPN eliminates the need for parentheses by using postfix notation. The stack naturally handles operator precedence because operators are applied immediately to their operands.
Common Mistakes
- Wrong operand order for non-commutative operations:
result = left - right # First popped is right operand
- Using floor division for truncation:
result = int(left / right) # Truncates toward zero
- Not handling negative numbers in input:
if token not in operators:
stack.append(int(token)) # int() handles negatives
- Returning wrong result:
return stack[0] # or stack.pop()
Interview Tips
Do's
- Clarify input format: "Are all tokens already split into a list?"
- Explain RPN concept: "In RPN, operators come after operands, so 3 + 4 becomes 3 4 +"
- Walk through an example: Draw the stack state after each token
Don'ts
- Don't confuse RPN with infix notation:
- Don't use
//for division: - Don't forget operand order:
12. Complexity Summary Table
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Stack (if/elif) | O(n) | O(n) | Always - simple and clear |
| Stack (operator dict) | O(n) | O(n) | When you need extensibility |
Optimal Solution: Stack with O(n) time and O(n) space.