</> MAANG.io
coding interview Β· 101

Foundations

Master coding interviews with comprehensive coverage of data structures, algorithms, and problem-solving techniques. Progress from fundamentals to advanced topics with expertly curated content.

0/255 solved 0% complete

Stack Implementations

What is this?

So far you have used a stack as a tool; here you build new gadgets out of stacks. For example, you can make a stack that instantly tells you its smallest value by keeping a second pile of plates that always has the current smallest on top. Or you can build a waiting line (where the first to arrive is the first served) out of two plate-piles facing each other. The trick is doing a tiny bit of bookkeeping on every move so the answer you want is always ready.

flowchart TD A["Build a structure from stacks"] --> B["Keep an extra helper stack"] A --> C["Combine two stacks"] B --> D["Min Stack with instant smallest"] C --> E["Queue made from two stacks"]

πŸ’‘ Fun fact: Two stacks placed back to back behave like a queue because reversing a pile twice puts it back in its original order β€” pour from one into the other and the oldest item ends up on top, ready to serve first.

πŸ”“ The 2 problems in this chapter are free. Sign in with Google or Microsoft to start solving.


Core idea: These are design problems β€” you don't use a stack to solve a puzzle, you build a data structure out of stacks. The trick each time is an auxiliary structure that maintains an invariant in lockstep with the main stack, so the operation you care about stays O(1).


A different kind of stack problem

Earlier chapters used a stack as a tool. Here the stack is the raw material, and the question is how to compose it into something more capable without losing O(1) speed. Two classic moves:

  • Track an aggregate alongside the data (Min Stack) β€” keep a parallel stack of "the min so far" so you never scan.
  • Compose two primitives into a new ADT (Queue Using Stacks) β€” turn two LIFO stacks into one FIFO queue with a lazy transfer.

Flowchart: stack-based design branches into augment-with-an-invariant (Min Stack) and compose-primitives (Queue from 2 stacks)


The two problems

Min Stack β€” O(1) getMin via a parallel invariant

Support push, pop, top, and getMin, all in O(1). The insight: the minimum of a stack is itself a stack β€” when you pop a value, the min must revert to whatever it was before that value was pushed. So keep a second "min stack" that, in lockstep, always has the current minimum on top.

Min Stack: parallel main and min stacks across push 5, push 3, push 7, pop; min stack keeps 3 on top and falls back to 3 after pop, getMin = min[-1] in O(1)
β†’ every operation O(1), space O(n).

Queue Using Stacks β€” FIFO from two LIFOs, amortized O(1)

A stack reversed twice is back in original order β€” so use an in stack for pushes and an out stack for pops. Pour in β†’ out only when out is empty; that reversal restores arrival order, and each element is moved at most once.

Queue Using Stacks pseudocode: push x appends to in; pop/peek pours in into out only when out is empty (the lazy pour), then returns out.pop() or out[-1], amortized O(1)
Each element is pushed to in once, moved to out once, popped once β€” three touches total β†’ amortized O(1) per operation.


The cross-cutting skill: maintain an invariant cheaply

Problem Invariant Kept by
Min Stack min_stack[-1] = min of all current elements push/pop the min in lockstep
Queue Using Stacks out (reversed) holds the oldest elements on top lazy pour only when out empties

The pattern is "spend a little work on every mutation to keep an invariant true, so the hot query stays O(1)." Min Stack pays one extra push/pop; the queue pays a bulk pour, but rarely β€” the heart of amortized analysis.


Where you'll meet it beyond the interview

  • Running aggregates under undo β€” a stack of editor states that also reports the cheapest/min state.
  • ADT adaptation β€” building the interface you need (FIFO) on top of the primitive you're given (LIFO), the everyday reality of constrained APIs.
  • Amortized hot paths β€” deferring a bulk cost (the pour, a resize, a flush) so the common operation stays cheap β€” the same reasoning behind dynamic-array growth and write buffers.

πŸ““ Draw it yourself

  1. Two parallel stacks. For Min Stack, draw the value stack and the min stack side by side; push a few values (including a new min and a duplicate), then pop the min and watch the min column fall back.
  2. The lazy pour. For Queue Using Stacks, draw in and out; push 3, then pop β€” draw the one-time pour from in into out (reversing), and note the next pop touches nothing.

Snap photos and embed them with the /host-diagrams skill.


Complexity at a glance

Problem Per-op time Space
Min Stack O(1) all ops O(n)
Queue Using Stacks O(1) amortized O(n)

Key takeaways

  • These are design problems β€” build a structure from stacks rather than solving with one.
  • Min Stack: the min is itself a stack; track it in lockstep for O(1) getMin.
  • Queue from two stacks: reverse twice via a lazy pour; each element moves once β†’ amortized O(1).
  • Amortized β‰  per-op: a rare bulk cost (the pour) keeps the common path cheap β€” be ready to prove it.
  • Why this chapter matters: it tests whether you can extend and compose data structures and reason about amortized cost β€” exactly the judgment real systems work demands.

Order: Min Stack (augment with an invariant) β†’ Queue Using Stacks (compose + amortized analysis) β†’ Design Browser History (two stacks as a timeline).

Min Stack

Core idea: the minimum of a stack is itself a stack β€” every time you push a value, remember what the minimum was at that moment, so popping naturally rewinds the minimum too.

Problem, rephrased

Imagine you're building the undo history for a photo editor. Every edit the user makes (brightness +10, crop, rotate) gets pushed onto a stack. Undo pops the most recent edit. So far, that's a plain stack.

Now your product manager adds a twist: there's a little badge in the corner that always shows the cheapest "render cost" among all edits currently on the undo stack β€” a hint for the user about how heavy their pending changes are. That badge needs to update instantly, even after an undo, and it must never lag.

So you need a stack that supports the usual operations plus an instant "what's the smallest value currently in here?" β€” and all four must run in O(1):

Operation Meaning
push(x) put x on top
pop() remove the top element
top() read the top element (don't remove it)
getMin() read the smallest element anywhere in the stack

The hard part is getMin(). After you pop the smallest element, the answer has to fall back to whatever the smallest element was before β€” without rescanning everything.

Here's a concrete trace to anchor the behavior:

Step Operation Stack (bottom→top) Result
1 push(8) 8
2 push(3) 8 3
3 push(5) 8 3 5
4 getMin() 8 3 5 3
5 push(2) 8 3 5 2
6 getMin() 8 3 5 2 2
7 pop() 8 3 5 (removed 2)
8 getMin() 8 3 5 3 ← the min resurfaced
9 top() 8 3 5 5

Notice step 8: popping 2 made the minimum revert to 3 automatically. That "revert" is the whole problem.

Sign in to continue reading

The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.

Sign in to read the full lesson

Sign in to MAANG.io

Use your Google or Microsoft account β€” no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.