Stack
Use a last-in-first-out stack to match pairs, cancel neighbours, evaluate expressions and simulate nesting in a single left-to-right pass.
Instinct Things resolve in reverse order of arrival (brackets, adjacent pairs, nested expressions) - push now, pop when its partner shows up.
Memory hack Last in, first out - like a pile of plates, you only ever touch the one on top; every closer pops its opener.
At a glance
When to use it
- Brackets, tags or other pairs must match in nested order
- Adjacent items cancel each other as you scan
- You evaluate a postfix or infix expression
- The newest unfinished item is always the next one to resolve
- You need undo, backspace or a stack that tracks its minimum
Key idea
Push each pending item and pop it when its partner arrives, so the top is always the most recent unresolved item; every element is pushed and popped at most once, giving a single O(n) pass instead of O(n^2) rescans.
Typical complexity
O(n) time, O(n) space
| # | Common problems | Level | Our solution | Practice |
|---|---|---|---|---|
| 1 | Valid Parentheses | Easy | Read solution | LeetCode ↗ |
| 2 | Remove All Adjacent Duplicates In String | Easy | Read solution | LeetCode ↗ |
| 3 | Min Stack | Medium | Read solution | LeetCode ↗ |
| 4 | Evaluate Reverse Polish Notation | Medium | Read solution | LeetCode ↗ |
| 5 | Basic Calculator II | Medium | Read solution | LeetCode ↗ |
| 6 | Longest Valid Parentheses | Hard | Read solution | LeetCode ↗ |
The instinct
A stack shines when the most recent unfinished thing is the one that matters next. An open bracket waits for its closer, a letter waits to see if its twin arrives to cancel it, an operand waits for its operator, and an outer expression waits while an inner one is evaluated. Each time you scan left to right and the newest pending item is the only candidate to resolve, a stack turns a messy nested problem into one linear pass.
Recognise it
| Signal in the problem | Why it points here |
|---|---|
| "valid parentheses", balanced brackets, matching tags | Each closer must match the most recent unmatched opener. |
| "repeatedly remove adjacent duplicates" / "a star removes the closest char to its left" | The top of the stack is the left neighbour after all earlier removals. |
| "evaluate Reverse Polish Notation" / postfix | Operands wait on the stack until an operator consumes the top two. |
| "basic calculator" with + - * / or parentheses | Defer low-precedence work; push signed terms or save state at each (. |
| "decode k[encoded] / nested structure / simplify a path" | Nesting depth maps one-to-one to stack depth. |
| "design a stack that also returns the minimum in O(1)" | Store the running minimum alongside each pushed value. |
| "undo, backspace, browser back button" | Undo always reverses the latest action. |
| "smallest result after removing letters/digits, keep order" | Greedy pop while the top is worse and can still be dropped (this edges into monotonic stack). |
Not this pattern if...
- You need the next greater / smaller element for every index - that is the specialised monotonic stack.
- First come, first served (level order, sliding window max from the front) - use a queue or deque.
- You only need a count of open brackets of a single type - a plain integer counter is enough.
Memory hack
"Last in, first out - every closer pops its opener." Think of a stack of cafeteria plates: you add to the top and take from the top. A closing bracket is a hungry diner who can only take the top plate - if it is the wrong shape, the input is invalid.
Analogy: Russian nesting dolls. You open dolls from the outside in (push), but you must close them from the inside out (pop). Any time a problem describes something closing in the reverse order it opened, picture the dolls.
Say it out loud in the interview:
- "What do I push - the character, its index, a number, or a saved state?"
- "What event triggers a pop, and what do I check when I pop?"
- "What does the stack mean at the end - empty for valid, or the remaining items are the answer?"
How it works
Scan the input once. For each token decide: push (it opens or waits), pop and combine (it closes or completes something), or pop and discard (it cancels). The final stack contents, or whether it is empty, is the answer.
Worked trace for Valid Parentheses on "{[()]}":
| Char | Action | Stack after |
|---|---|---|
{ |
push | { |
[ |
push | { [ |
( |
push | { [ ( |
) |
pop ( - matches |
{ [ |
] |
pop [ - matches |
{ |
} |
pop { - matches |
empty -> valid |
For Basic Calculator II, push each term with its sign: +x pushes x, -x pushes -x, and * or / pop the top, combine it with the new number, and push the result. Summing the stack at the end applies the remaining additions, because the higher-precedence work has already been done.
For Longest Valid Parentheses, push indices instead of characters, and keep a base index at the bottom: the length of a valid run is i - stack.peek() after a successful pop.
Java template
import java.util.*; class StackKit { // Template 1: matching pairs with an ArrayDeque used as a stack. static boolean isValid(String s) { Deque<Character> st = new ArrayDeque<>(); for (char c : s.toCharArray()) { if (c == '(') st.push(')'); // push the EXPECTED closer else if (c == '[') st.push(']'); else if (c == '{') st.push('}'); else if (st.isEmpty() || st.pop() != c) return false; } return st.isEmpty(); } // Template 2: cancel adjacent items; StringBuilder doubles as a char stack. static String removeAdjacent(String s) { StringBuilder sb = new StringBuilder(); for (char c : s.toCharArray()) { int n = sb.length(); if (n > 0 && sb.charAt(n - 1) == c) sb.deleteCharAt(n - 1); // problem-specific cancel rule else sb.append(c); } return sb.toString(); } // Template 3: evaluate postfix tokens. static int evalRPN(String[] tokens) { Deque<Integer> st = new ArrayDeque<>(); for (String t : tokens) { switch (t) { case "+" -> st.push(st.pop() + st.pop()); case "*" -> st.push(st.pop() * st.pop()); case "-" -> { int b = st.pop(), a = st.pop(); st.push(a - b); } case "/" -> { int b = st.pop(), a = st.pop(); st.push(a / b); } default -> st.push(Integer.parseInt(t)); } } return st.pop(); } } // Template 4: O(1) getMin by pairing each value with the min at that depth. class MinStack { private final Deque<int[]> st = new ArrayDeque<>(); void push(int x) { st.push(new int[]{x, st.isEmpty() ? x : Math.min(x, st.peek()[1])}); } void pop() { st.pop(); } int top() { return st.peek()[0]; } int getMin() { return st.peek()[1]; } }
Complexity
| Use | Time | Space |
|---|---|---|
| Matching / cancelling in one pass | O(n) | O(n) worst case |
| Postfix or infix evaluation | O(n) | O(n) |
| Min stack operations | O(1) each | O(n) |
Each element is pushed once and popped at most once, so the total work stays linear even with an inner while loop.
Variations
- Pair matching - push openers (or expected closers): Valid Parentheses.
- Neighbour cancellation - top equals current, so both disappear: Remove All Adjacent Duplicates In String, Removing Stars From a String.
- Expression evaluation - operands on the stack: Evaluate Reverse Polish Notation, Basic Calculator II.
- Index stack with a base - lengths from index differences: Longest Valid Parentheses.
- Augmented stack - store extra info per level: Min Stack.
- Greedy stack with "seen" set - pop larger letters that reappear later: Remove Duplicate Letters.
- State save at nesting - push (count, string) or (result, sign) at each
[or(: Decode String, Basic Calculator.
Common mistakes
Calling pop() or peek() on an empty stack. A lone closer such as ")" must return false, not throw. Check isEmpty() before every pop.
- Forgetting the final
isEmpty()check -"(("never fails during the scan but is still invalid. - Using
java.util.Stack(synchronised, extendsVector) whenArrayDequeis faster and idiomatic; rememberArrayDequerejectsnull. - Popping operands in the wrong order for
-and/: the first pop is the right operand. - Comparing boxed
Integerobjects with==from two stacks - values above 127 are different objects; use.equalsor unbox. - In calculators, forgetting to apply the last pending number when the string ends.
All practice problems
8 problems on this site use this pattern, easiest first. Solve them in order and say the instinct out loud before you code.