Evaluate Reverse Polish Notation

Medium Stacks

Problem

Given an array of strings tokens representing an arithmetic expression in Reverse Polish Notation, return the evaluation of the expression.

  • 1 ≤ tokens.length ≤ 10⁴
  • tokens[i] is either an operator: '+', '-', '*', or '/', or an integer in string format
  • The given RPN expression is always valid

Example

Input: tokens = ["2", "1", "+", "3", "*"]
Output: 9

The expression corresponds to ((2 + 1) * 3). The algorithm processes tokens sequentially: push 2 and 1 onto the stack, encounter '+', pop 1 and 2, compute 2 + 1 = 3, push 3 back. Next, push 3, encounter '*', pop 3 and 3, compute 3 * 3 = 9, push 9. The final stack top is 9, the result.

Approach

Straightforward Solution

A naive approach might try to recursively parse the expression or convert it to infix notation, which is complex and inefficient. Alternatively, scanning tokens and evaluating operators immediately without a stack would fail to maintain correct operand order.

Core Observation

Reverse Polish Notation expressions can be evaluated using a stack because each operator applies to the two most recent operands. This last-in-first-out property aligns naturally with stack operations.

Path to Optimal

The key recognition signals are 'Reverse Polish Notation', 'operators apply to previous two operands', and 'evaluate expression'. These indicate the Stack pattern because the problem requires last-in-first-out access to operands. By pushing operands onto a stack and applying operators to the top two elements, the algorithm maintains correct evaluation order and handles nested expressions naturally.

Optimal Approach

Iterate through tokens. For operands, convert to integer and push onto the stack. For operators, pop the top two operands, apply the operator in correct order (paying attention to subtraction and division), and push the result back. After processing all tokens, the stack contains the final result. This approach runs in O(n) time with O(n) space.

Time

O(n)

Each token is processed exactly once. Stack push and pop operations are O(1), so total time is linear in the number of tokens.

Space

O(n)

In the worst case, all tokens are operands and pushed onto the stack, requiring O(n) auxiliary space proportional to input size.

Pattern Spotlight

Stack (Expression Evaluation)

Use a stack to store operands and apply operators to the top two elements, ensuring correct operand order and enabling linear-time evaluation of postfix expressions.

Solution

1class Solution:
2 def evalRPN(self, tokens: list[str]) -> int:
3 stack = []
4 for c in tokens:
5 if c == "+":
6 stack.append(stack.pop() + stack.pop())
7 elif c == "-":
8 a, b = stack.pop(), stack.pop()
9 stack.append(b - a)
10 elif c == "*":
11 stack.append(stack.pop() * stack.pop())
12 elif c == "/":
13 a, b = stack.pop(), stack.pop()
14 stack.append(int(b / a))
15 else:
16 stack.append(int(c))
17 return stack[0]

Step-by-Step Solution

1

Initialize an Empty Stack to Store Operands

3stack = []

Objective

To prepare a data structure that will hold operands and intermediate results during evaluation.

Key Insight

A stack perfectly models the evaluation order of Reverse Polish Notation expressions. By pushing operands and popping them when an operator is encountered, the algorithm maintains the correct order of operations without recursion or complex parsing.

Interview Quick-Check

Core Logic

The stack stores operands and intermediate results, enabling last-in-first-out access required by RPN evaluation.

Common Pitfalls & Bugs

Using a queue or list without stack semantics would fail to maintain correct operand order.

2

Iterate Through Tokens and Evaluate Expression Using Stack Operations

4for c in tokens:
5 if c == "+":
6 stack.append(stack.pop() + stack.pop())
7 elif c == "-":
8 a, b = stack.pop(), stack.pop()
9 stack.append(b - a)
10 elif c == "*":
11 stack.append(stack.pop() * stack.pop())
12 elif c == "/":
13 a, b = stack.pop(), stack.pop()
14 stack.append(int(b / a))
15 else:
16 stack.append(int(c))

Objective

To process each token, pushing operands or applying operators to the top two operands on the stack.

Key Insight

The algorithm distinguishes operators from operands. For operators, it pops the top two operands, applies the operator in the correct order (important for '-' and '/'), and pushes the result back. For operands, it converts the string to an integer and pushes it. This approach ensures correct evaluation order and handles all valid RPN expressions in a single pass.

Interview Quick-Check

Core Logic

For each operator, pop the top two operands, apply the operator with correct operand order, and push the result; for operands, convert and push.

State & Boundaries

Ensure the stack always has at least two operands before applying an operator to avoid runtime errors.

Common Pitfalls & Bugs

Incorrect operand order for subtraction and division leads to wrong results; the second popped operand is the left operand.

Complexity

Each token is processed once, and stack operations are O(1), resulting in O(n) time complexity.

3

Return the Final Result from the Stack

17return stack[0]

Objective

To output the evaluated integer result after processing all tokens.

Key Insight

After processing all tokens, the stack contains exactly one element: the final evaluation result. Returning this element completes the evaluation process.

Interview Quick-Check

Core Logic

The final stack element is the result of the entire RPN expression evaluation.

State & Boundaries

The stack should contain exactly one element at this point; otherwise, the input expression was invalid.

Line Analysis

This solution has 6 Critical lines interviewers watch for.

Line 6 Critical
stack.append(stack.pop() + stack.pop())

Pop the top two operands, add them, and push the result back onto the stack.

This line performs the core arithmetic operation for addition, combining the two most recent operands and maintaining the stack state for further evaluation.

Line 11 Critical
stack.append(stack.pop() * stack.pop())

Pop the top two operands, multiply them, and push the result.

This line executes the multiplication operation and maintains the stack state for further processing.

Line 14 Critical
stack.append(int(b / a))

Perform integer division with truncation toward zero and push the result.

Using int(b / a) ensures division truncates toward zero as required, which differs from floor division and is critical for correctness.

Line 8 Critical
a, b = stack.pop(), stack.pop()

Pop the top two operands in correct order for subtraction.

Popping operands in this order ensures the left operand is the second popped value, preserving correct subtraction semantics.

Line 13 Critical
a, b = stack.pop(), stack.pop()

Pop the top two operands in correct order for division.

Popping operands in this order ensures the left operand is the second popped value, preserving correct division semantics.

Line 17 Critical
return stack[0]

Return the final evaluated result from the top of the stack.

After processing all tokens, the stack contains exactly one element representing the fully evaluated expression, which is the correct output.

Line 3 Core
stack = []

Initialize an empty stack to hold operands and intermediate results.

The stack is essential for storing operands in a last-in-first-out manner, which aligns perfectly with the evaluation order of Reverse Polish Notation expressions.

Line 9 Core
stack.append(b - a)

Compute the difference (left operand minus right operand) and push the result.

This line applies subtraction correctly and updates the stack with the intermediate result, enabling continued evaluation.

Line 15 Core
else:

If the token is not an operator, convert it to an integer and push onto the stack.

This line handles operands by converting string tokens to integers and storing them for future operations, enabling the stack-based evaluation.

Line 5 Core
if c == "+":

Check if the current token is the addition operator '+'.

Identifying operators allows the algorithm to apply the correct arithmetic operation to the top two operands on the stack.

Line 7 Core
elif c == "-":

Check if the current token is the subtraction operator '-'.

Detecting subtraction is necessary to apply the correct operation with attention to operand order.

Line 10 Core
elif c == "*":

Check if the current token is the multiplication operator '*'.

Recognizing multiplication allows the algorithm to perform the corresponding arithmetic operation on the top two operands.

Line 12 Core
elif c == "/":

Check if the current token is the division operator '/'.

Identifying division is essential to apply the correct arithmetic operation with proper operand order and truncation.

Line 4 Standard
for c in tokens:

Begin iterating over each token in the input list.

Sequentially processing tokens ensures the algorithm respects the order of the RPN expression, which is critical for correct evaluation.

Line 16 Standard
stack.append(int(c))

End of the for loop iteration over tokens.

Completing the iteration ensures all tokens are processed in order, maintaining the integrity of the evaluation.

Test Your Understanding

Why must the operator apply to the two most recently pushed operands, and why is operand order important for subtraction and division?

Because Reverse Polish Notation defines operators to apply to the two immediately preceding operands, which are naturally accessed via a stack's last-in-first-out order. Operand order matters for subtraction and division since these operations are not commutative; the second popped operand is the left operand, and the first popped is the right operand.

Related Problems

Stacks pattern

Don't just read it. Drill it.

Reconstruct Evaluate Reverse Polish Notation from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.

Unlock the Evaluate Reverse Polish Notation drill

or drill a free problem