Pattern 04 of 20

Stack

Last in, first out: match pairs and find the next greater or smaller element.

When to use it

  • Matching brackets, tags or nested structures
  • Each element needs the nearest larger or smaller element
  • Evaluating expressions or undoing the most recent step

The idea

A stack keeps the items that are still waiting for an answer. When a new item arrives it resolves everything on top that it answers, which is why a monotonic stack finds next-greater elements in one pass.

Typical cost O(n) time (each item is pushed and popped once), O(n) space.

How it works

A stack holds work that is still open: brackets waiting to be closed, operands waiting for an operator, elements waiting to learn who is bigger than them. The most recent open item is always the first one that can be resolved, which is exactly last in, first out.

The monotonic stack is the version that wins interviews. Keep the stack sorted (say, decreasing). When a new element arrives, every smaller element on top has just found its next greater element, so pop and answer them. Each element is pushed once and popped once, so the whole scan is linear.

Store indices rather than values whenever the answer is a distance or a width. The index gives you both the value (by lookup) and the position.

Disguises include daily temperature waits, stock spans, the largest rectangle in a histogram, removing digits to make the smallest number, and simplifying file paths. If each element needs its nearest bigger or smaller neighbor, reach for a monotonic stack.

How to attack it

  1. 1Ask what each element is waiting for; that is what sits on the stack.
  2. 2Decide the stack order (increasing or decreasing) from the question.
  3. 3Decide whether to store values, indices, or pairs.
  4. 4Write the pop condition and the answer recorded on each pop.
  5. 5Decide what to do with elements still on the stack at the end.

Template

A skeleton to adapt, not a solution to paste. Write it from memory a few times.

def next_greater(nums):
    # Answer for each index: the next value to its right that is larger.
    result = [-1] * len(nums)
    stack = []  # indices whose answer is not known yet; values decreasing
    for i, x in enumerate(nums):
        # x resolves every smaller element still waiting on the stack.
        while stack and nums[stack[-1]] < x:
            j = stack.pop()
            result[j] = x  # or i - j for a distance
        stack.append(i)
    # Whatever remains has no greater element to the right.
    return result


def is_balanced(s):
    pairs = {")": "(", "]": "[", "}": "{"}
    stack = []
    for c in s:
        if c in pairs:
            if not stack or stack.pop() != pairs[c]:
                return False
        else:
            stack.append(c)
    return not stack

Variants

Matching pairs
Push openers, pop on closers and compare. Brackets, HTML tags and collapsing adjacent duplicates.
Next greater or smaller
A monotonic stack of indices resolves each element when a larger (or smaller) one arrives.
Histogram areas
With an increasing stack, a popped bar's width runs from the new top to the current index; append a zero bar to flush the stack.
Expression evaluation
Push operands, apply operators on pop; two stacks (values and operators) handle precedence and parentheses.
Min stack
Store the current minimum alongside each pushed value so getMin stays O(1) after pops.

Pitfalls

  • Popping or peeking an empty stack.
  • Storing values when the answer needs distances, then having no way to compute them.
  • Choosing < versus <= without thinking about equal elements.
  • Forgetting to process elements left on the stack after the loop.
  • Reading the top with stack[-1] in JavaScript, which is undefined; use stack[stack.length - 1].

What it costs

OperationCost
Push / pop / peekO(1)
Monotonic stack passO(n) time amortized, O(n) space
Bracket validationO(n) time, O(n) space
Brute force next greaterO(n²) time

30 Stack problems

Easiest first. Problems marked Free can be solved without Pro.

  1. 1Bracket Balance CheckFreeEasy
  2. 2Typing With BackspaceFreeEasy
  3. 3Collapse Matching NeighborsFreeEasy
  4. 4Cash Register LogEasy
  5. 5Fewest Parentheses to InsertEasy
  6. 6Clearance DiscountsEasy
  7. 7Deepest Nesting LevelEasy
  8. 8Peel the Outer LayerEasy
  9. 9Next Larger From a Reference ListEasy
  10. 10Days Until WarmerMedium
  11. 11Evaluate a Postfix ExpressionMedium
  12. 12Normalize a File PathMedium
  13. 13Expand Repeat CodesMedium
  14. 14Colliding ParticlesMedium
  15. 15Smallest Number After DeletionsMedium
  16. 16Next Larger on a RingMedium
  17. 17Price Run LengthsMedium
  18. 18Replay a Push-Pop LogMedium
  19. 19Total of Window MinimumsMedium
  20. 20Bracket ScoreMedium
  21. 21Convoys to the FinishMedium
  22. 22Four-Function CalculatorMedium
  23. 23Dip Pattern SearchMedium
  24. 24Smallest Distinct-Letter SubsequenceMedium
  25. 25Largest Banner Under the SkylineHard
  26. 26Longest Well-Formed StretchHard
  27. 27Visible Heads in a QueueHard
  28. 28Rain Pooled Between WallsHard
  29. 29Largest Lit RectangleHard
  30. 30Calculator With ParenthesesHard

Solve them in the browser, in Python or JavaScript.