Theory · Stacks
DSA Roadmap Open Visualizer Back to intro

Algocraft · Theory

Stacks: last in, first out — and why that's powerful

A stack is the natural fit whenever a problem needs to "remember what came before, but only care about the most recent unresolved thing." That shows up in three flavors: nested structures that must close in reverse order of how they opened, undo/redo history where the last action is the first to unwind, and — the pattern that shows up constantly in interviews — scanning an array while looking backward for the first thing that breaks a threshold. That last one is what turns a plain stack into a monotonic stack, and it's most of what this page is about.

01 · Foundation

The LIFO idea

A stack supports exactly two moves: push (add on top) and pop (remove from the top). Nothing else is reachable — you can't grab the item underneath without removing everything above it first. That constraint sounds limiting, but it's precisely what makes a stack the right tool for a specific family of problems: anything where the most recently opened thing must be the first one closed.

Think about matching parentheses in { [ ( ) ] }. The ( that opened last is the one the very next ) has to close — not the { or the [, even though they opened earlier. HTML/XML tags nest the same way: <div><span>...</span></div> — the innermost, most recently opened tag closes first. Undo/redo history behaves identically: the last action you took is the first one "Undo" reverses.

The tell: if a problem talks about nesting, matching pairs, or "undo the most recent thing," you're looking at a plain LIFO stack. Push when something opens/happens; pop and check when something should close/reverse. If a pop ever finds the stack empty, or something's left on the stack once you're done scanning, that's your signal for "invalid" or "unresolved."

02 · The pattern that shows up everywhere

The monotonic stack pattern

A monotonic stack is a plain stack with one extra rule enforced at all times: the values inside it are kept strictly increasing (or strictly decreasing) from bottom to top. The moment a new element would break that order, you don't just push it on top — you first pop off every element it violates, and each pop usually is the answer for the element being popped.

This is the mechanism behind Next Greater Element: keep a decreasing stack of "elements still waiting for something bigger to show up." When the current number is bigger than the stack's top, that top element has just found its answer — pop it, record the current number as its next-greater value, and repeat until the stack top is bigger than (or the stack is empty), then push the current element. The same shape — pop while a condition holds, then push — also drives sliding-window-maximum-style problems, where the stack (or deque) tracks "candidates that could still be the max of some future window."

After index 1 i0 = 2 i1 = 1 bottom → top: 2, 1 After index 2 — pop triggered i0 = 2 i2 = 2 bottom → top: 2, 2 i1 = 1 popped: NGE[1]=2 Final stack i3 = 4 i4 = 3 indices 3, 4 unresolved

Stack shown bottom → top, values kept decreasing. When index 2's value (2) arrives, it breaks the order set by index 1's value (1) — so index 1 is popped (orange) and its answer becomes NGE[1] = 2, then index 2 is pushed. By the time the array ends, indices 3 and 4 are still sitting on the stack because nothing bigger ever showed up for them — their answer is -1.

Notice the direction: a decreasing stack (top is always the smallest unresolved value) is what you want when searching for the next greater element ahead. Flip it to an increasing stack if you're searching for the next smaller element instead — the pop condition's comparison simply flips too.

03 · Method

A repeatable framework

Work through these five steps, in order, for any "stack-shaped" problem — plain LIFO or monotonic.

  1. 1 Confirm it's a stack problemThe tell is "look back until something breaks a condition" — a closing bracket needs the most recent open one, a bigger number invalidates every smaller number still waiting behind it. If you need arbitrary random access into the history, a stack isn't enough.
  2. 2 Decide what to storeThe value itself, its index, or both. Store the index whenever you'll later need a distance or gap (e.g. "how many days until a warmer temperature") — you can always look the value up via the index, but you can't recover the index from a bare value.
  3. 3 Define the push/pop trigger preciselyWrite the exact condition, e.g. "pop while stack top's value < current value" for a next-greater search. Get the comparison direction and the strict/non-strict boundary (< vs ≤) right — this is where most bugs live.
  4. 4 Process left to rightAt each element, apply the pop trigger in a loop (possibly zero times, possibly several) until it no longer holds, then push the current element.
  5. 5 Handle what's left at the endAnything still on the stack when the scan finishes never found what it was waiting for — usually that means "no answer" (-1, or "invalid input" for unmatched brackets).

04 · Worked example

Next Greater Element, one index at a time

Array [2, 1, 2, 4, 3]. Stack holds indices (so we can report the answer against the original position), kept decreasing by value. Trigger: while the stack is non-empty and arr[stack.top] < arr[i], pop it and record arr[i] as its next-greater value — then push i.

indexvaluestack after this step (bottom → top)answer recorded this step
02[0:2]
11[0:2, 1:1]
22[0:2, 2:2]NGE[1] = 2
34[3:4]NGE[2] = 4, NGE[0] = 4
43[3:4, 4:3]
end[3:4, 4:3] left overNGE[3] = -1, NGE[4] = -1

At index 2, value 2 breaks the order set by index 1's value 1 (1 < 2), so index 1 pops with answer 2, and then index 0's value 2 is not less than 2 (equal doesn't count), so it stays and index 2 gets pushed. At index 3, value 4 is bigger than both remaining stack values (2 and 2), so both pop in the same step. Indices 3 and 4 never get popped by anything — they're still on the stack when the array ends, so their answer defaults to -1.

Final result: NGE = [4, 2, 4, -1, -1]. Open the Next Greater Element visualizer to watch this same trace animate step by step.

05 · Complexity

The amortized O(n) insight

A monotonic stack's code has a while loop nested inside a for loop, which looks like it could blow up to O(n²) in the worst case. It doesn't — and the reason is worth calling out explicitly, because it's easy to misjudge at a glance.

Every index is pushed exactly once across the entire run (the for loop pushes each index a single time), and every index can be popped at most once (once it's off the stack, it's gone for good). So across the whole algorithm, the total number of push operations is n, and the total number of pop operations is also bounded by n — never more than what was pushed. That caps total stack operations at roughly 2n, not .

This is amortized analysis: a single iteration's inner while loop might run many times (or zero times), but averaged — "amortized" — over the whole array, each element does only O(1) total work across the entire algorithm. The overall time complexity is O(n), and space is O(n) for the stack itself.

06 · A neat trick

Min Stack — an auxiliary-stack trick

A plain stack gives you O(1) access to the top, but not to the minimum — scanning the whole stack for min() every time getMin() is called costs O(n). The fix is to keep a second stack whose job is only to track "what was the minimum at this point in history."

The simplest robust version: whenever you push a value onto the main stack, push a pair (value, min(value, currentMinSoFar)) — so every frame carries a snapshot of what the minimum was at that moment. getMin() just peeks at the top frame's second element, in O(1). Popping is automatically consistent, because the frame beneath always remembers the minimum that was true before the popped value ever arrived — you never have to "recompute" a minimum after a pop.

Try tracing it yourself in the Min Stack visualizer to see the auxiliary stack update in lockstep with the main one.

07 · Before you start coding

Common mistakes

08 · Practice

Try it in the visualizers

Each card below links to the matching interactive visualizer so you can watch the stack fill and drain in real time.