23.Evaluate Reverse Polish Notation
Problem
You are given an array of strings tokens describing an arithmetic expression in Reverse Polish Notation (postfix): every operator comes after its two operands. Evaluate the expression and return its integer value.
- Valid operators are
+,-,*and/. - Each operand is an integer (it may be negative).
- Division truncates toward zero.
- The input is always a valid expression, there is no division by zero, and every intermediate result fits in a 32-bit integer.
Examples
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: 22
Explanation: 9 + 3 = 12; 12 * -11 = -132; 6 / -132 = 0 (truncated);
10 * 0 = 0; 0 + 17 = 17; 17 + 5 = 22Constraints
1 <= tokens.length <= 10^4tokens[i]is an operator ("+","-","*","/") or an integer in[-200, 200].
๐ก Key Insight
In postfix notation, an operator always applies to the two most recently produced values. A stack holds exactly those: push numbers; on an operator, pop two values, combine them, and push the result back. Mind the order โ the first value popped is the right operand.
Brute Force (Repeated Reduction)
TimeO(nยฒ)SpaceO(n)Intuition
Find the first operator in the list. The two tokens immediately before it are guaranteed to be numbers (otherwise a previous operator would come first). Replace those three tokens with their result and repeat until a single number remains.
Algorithm
- Copy
tokensinto a mutable list. - While the list has more than one element:
- Find the first index
iholding an operator. - Compute
list[i-2] op list[i-1]. - Replace positions
i-2..iwith the result.
- Find the first index
- Return the single remaining value.
Visualization
[2, 1, +, 3, *]
^ first operator โ 2 + 1 = 3
[3, 3, *]
^ โ 3 * 3 = 9
[9] โ answer 9Code
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class Solution {
public int evalRPN(String[] tokens) {
List<String> list = new ArrayList<>(Arrays.asList(tokens));
while (list.size() > 1) {
int i = 0;
while (!isOperator(list.get(i))) {
i++; // first operator in the list
}
int a = Integer.parseInt(list.get(i - 2));
int b = Integer.parseInt(list.get(i - 1));
int result = apply(a, b, list.get(i));
// replace "a b op" with the result
list.remove(i);
list.remove(i - 1);
list.set(i - 2, String.valueOf(result));
}
return Integer.parseInt(list.get(0));
}
private boolean isOperator(String t) {
return t.equals("+") || t.equals("-") || t.equals("*") || t.equals("/");
}
private int apply(int a, int b, String op) {
switch (op) {
case "+": return a + b;
case "-": return a - b;
case "*": return a * b;
default: return a / b; // Java truncates toward zero
}
}
}Complexity
- Time: O(nยฒ) โ up to
n / 2reductions, each scanning and shifting anArrayList. - Space: O(n) โ the mutable copy.
Recursion from the End
TimeO(n)SpaceO(n)Intuition
Read the expression backwards: the last token is the root of the expression tree. If it is a number, that is the value. If it is an operator, its right operand is the expression that ends just before it, and its left operand is the expression before that. Recursion naturally consumes tokens in that order.
Algorithm
- Keep an index
posstarting at the last token. eval(): taketokens[pos--].- If it is a number, return it.
- Otherwise
right = eval(), thenleft = eval(), returnleft op right.
- Return
eval().
Dry Run
tokens = [2, 1, +, 3, *]
eval: '*' โ right = eval โ 3
left = eval: '+' โ right = eval โ 1
left = eval โ 2
โ 2 + 1 = 3
โ 3 * 3 = 9Code
class Solution {
private String[] tokens;
private int pos;
public int evalRPN(String[] tokens) {
this.tokens = tokens;
this.pos = tokens.length - 1;
return eval();
}
private int eval() {
String t = tokens[pos--];
if (!(t.equals("+") || t.equals("-") || t.equals("*") || t.equals("/"))) {
return Integer.parseInt(t); // leaf: a number
}
int right = eval(); // right operand ends just before the operator
int left = eval();
switch (t) {
case "+": return left + right;
case "-": return left - right;
case "*": return left * right;
default: return left / right;
}
}
}Complexity
- Time: O(n) โ each token is visited exactly once.
- Space: O(n) โ recursion depth equals the expression tree height (up to ~
n / 2for a skewed expression).
StackOptimal
TimeO(n)SpaceO(n)Intuition
Process tokens left to right. Numbers wait on a stack until an operator needs them; an operator pops its two operands, computes, and pushes the result, which may itself become an operand later. At the end exactly one value โ the answer โ remains.
Algorithm
- Create an empty stack.
- For each token:
- If it is an operator:
b = pop(),a = pop(), pusha op b. - Else push
Integer.parseInt(token).
- If it is an operator:
- Return
pop().
Dry Run
flowchart LR
A["'4'<br/>stack [4]"] --> B["'13'<br/>stack [4, 13]"]
B --> C["'5'<br/>stack [4, 13, 5]"]
C --> D["'/': b=5, a=13<br/>13 / 5 = 2<br/>stack [4, 2]"]
D --> E["'+': b=2, a=4<br/>4 + 2 = 6<br/>stack [6]"]
E --> F["return 6"]Code
import java.util.ArrayDeque;
import java.util.Deque;
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new ArrayDeque<>();
for (String t : tokens) {
switch (t) {
case "+" -> stack.push(stack.pop() + stack.pop());
case "*" -> stack.push(stack.pop() * stack.pop());
case "-" -> {
int b = stack.pop(); // right operand is on top
int a = stack.pop();
stack.push(a - b);
}
case "/" -> {
int b = stack.pop();
int a = stack.pop();
stack.push(a / b); // truncates toward zero
}
default -> stack.push(Integer.parseInt(t));
}
}
return stack.pop();
}
}Complexity
- Time: O(n) โ each token is pushed/popped a constant number of times.
- Space: O(n) โ at most about
n / 2 + 1numbers wait on the stack.
Complexity Comparison
| Approach | Time | Space |
|---|---|---|
| Brute Force (repeated reduction) | O(nยฒ) | O(n) |
| Recursion from the end | O(n) | O(n) |
| Stack | O(n) | O(n) |
Common Pitfalls
- Popping operands in the wrong order for
-and/: the first pop is the right operand (a - b, notb - a). - Detecting operators with
t.charAt(0) == '-'โ negative numbers like"-11"also start with'-'; compare the whole string (or check length 1). - Using
Math.floorDivโ the problem wants truncation toward zero, which is what plain/does in Java (-7 / 2 == -3). - Comparing strings with
==instead ofequals(theswitchonStringusesequalsunder the hood).