A stack is last-in, first-out (LIFO): the most recent item you pushed is the first one you get back. Reach for one whenever a problem has the feel of processing the latest one first: the most recent unclosed bracket, the last character typed, the last score recorded. With a little extra state per entry, a stack can also answer questions like "what is the minimum right now?" in .
The idea#
Many string and simulation problems have nested or "undo" structure. In all of them, the thing that decides what happens to the current item is the most recent unresolved item, which is exactly the top of a stack.
| Situation | Push | On the current item |
|---|---|---|
| Matching pairs (brackets, tags) | each opener | a closer must match the popped top |
| Undo / backspace | each kept item | an undo token pops the top |
| Cancel adjacent pairs | each item | if it equals or cancels the top, pop instead of pushing |
| Collisions | survivors so far | resolve against the top in a loop until stable |
Each item is pushed and popped at most once, so all of these run in .
So far each entry is a plain item. A design stack goes one step further and stores extra state in each entry. The entry records a fact about everything beneath it, so the fact is still correct after any pop.
How to spot it#
The latest item decides what to do with the current one. Look for these signals.
- Brackets, tags, or anything that opens and closes and can nest.
- "Remove adjacent duplicates" or "backspace" where a removal can expose a new match (Candy Crush style).
- An operation that refers to "the previous" or "the last two" results.
- A design question asking for
get_min,get_maxor "most frequent" in on top of push and pop.
Template#
This is the matching shape. The cancel, undo and collision shapes change only what happens when the current item meets the top.
stack = empty
for each item in input:
if item closes or acts on earlier work:
if stack is empty: fail or skip # nothing to match
top = pop from stack
if top does not fit item: fail # wrong match
else:
push item
if stack is not empty: fail # leftovers never closed
succeed
Notice the three ways to fail: a closer with nothing open, a closer of the wrong type, and openers left over at the end.
Pitfalls#
Most broken stack solutions come down to one of these.
- Popping an empty stack. Check
stackbeforestack.pop()orstack[-1]whenever the input can be malformed. - Forgetting the final check. Input like
"(("never fails a match. Only the leftover check catches it. - Using
ifinstead ofwhilewhen one item can resolve many. One incoming item can pop several entries in a row. - Over-engineering design stacks. An entry that looks stale is often exactly right for a later pop. Check before you add cleanup code.
Problems in this pattern#
These problems use this pattern. Solve them before you open the hints below. Start with Valid Parentheses.
- 20. Valid Parentheses
- 1047. Remove All Adjacent Duplicates In String
- 844. Backspace String Compare
- 682. Baseball Game
- 232. Implement Queue using Stacks
- 225. Implement Stack using Queues
- 155. Min Stack
- 1249. Minimum Remove to Make Valid Parentheses
- 735. Asteroid Collision
- 32. Longest Valid Parentheses
- 895. Maximum Frequency Stack
Hints#
Open a hint only after you have tried the problem for a while.
Hint: Valid Parentheses
Map each closer to its opener. Push openers; on a closer, the popped top must be its opener. Return not stack at the end.
Hint: Remove All Adjacent Duplicates In String
If the character equals the top, pop; otherwise push. A removal can expose a new pair, and the next character checks it automatically.
Hint: Backspace String Compare
Build each string with a stack where # pops (if anything is there). Then compare the two stacks with ==.
Hint: Baseball Game
Numbers push, C pops, D pushes double the top, and + pushes the sum of the top two. The answer is sum(stack).
Hint: Min Stack
For each entry, store its value and the minimum up to that point: (val, min(val, previous_min)). Then get_min is the top's second field, and popping restores the old minimum for free.
Hint: Minimum Remove to Make Valid Parentheses
Keep a ) only if an unmatched ( exists. Any ( still unmatched at the end is extra. Drop those scanning from the right, because the rightmost ones have no closer after them.
Hint: Asteroid Collision
Only a positive top with a negative incoming asteroid collides; every other sign pair is pushed. On a collision the smaller one explodes, and equal sizes destroy both. Resolve in a while loop, because one asteroid can destroy several.
Hint: Longest Valid Parentheses
Push (char, index). When a ) matches a ( on top, mark both indices as valid. The answer is the longest run of consecutive marked indices: one stack pass plus one linear scan.
Hint: Maximum Frequency Stack
Keep freq[val] and a stack per frequency, group[f]. Push adds val to group[freq[val]]; pop takes from group[max_freq]. A value lives in several groups at once, and that's correct: its copy in group 1 is the one to pop next at frequency 1.
Hint: Implement Queue using Stacks
Push onto an "in" stack. Pop from an "out" stack, and refill it from "in" only when it's empty. Each item moves at most once, so operations are amortized .
Hint: Implement Stack using Queues
With one queue: after appending a new item, rotate the queue len - 1 times so the new item comes to the front. Then pop and top read from the front.
Key takeaways#
- If the latest unresolved item decides what happens next, use a stack.
- Matching fails three ways: nothing to match, wrong match, leftovers at the end.
- Push indices when you need positions, not only values.
- Store extra state in each entry (running min, frequency group) for queries.